1 /* $OpenBSD: radiusd_bsdauth.c,v 1.7 2015/12/05 13:22:32 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 #include <sys/uio.h> 23 #include <sys/wait.h> 24 25 #include <bsd_auth.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <grp.h> 29 #include <imsg.h> 30 #include <login_cap.h> 31 #include <pwd.h> 32 #include <stdbool.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <syslog.h> 37 #include <unistd.h> 38 39 #include "radiusd.h" 40 #include "radiusd_module.h" 41 42 struct module_bsdauth { 43 struct module_base *base; 44 struct imsgbuf ibuf; 45 char **okgroups; 46 }; 47 48 /* IPC between priv and main */ 49 enum { 50 IMSG_BSDAUTH_OK = 1000, 51 IMSG_BSDAUTH_NG, 52 IMSG_BSDAUTH_USERCHECK, 53 IMSG_BSDAUTH_GROUPCHECK 54 }; 55 struct auth_usercheck_args { 56 size_t userlen; 57 size_t passlen; 58 }; 59 struct auth_groupcheck_args { 60 size_t userlen; 61 size_t grouplen; 62 }; 63 64 static void module_bsdauth_main(int, int); 65 static void module_bsdauth_config_set(void *, const char *, int, 66 char * const *); 67 static void module_bsdauth_userpass(void *, u_int, const char *, 68 const char *); 69 __dead static void 70 fatal(const char *); 71 72 static struct module_handlers module_bsdauth_handlers = { 73 .userpass = module_bsdauth_userpass, 74 .config_set = module_bsdauth_config_set 75 }; 76 77 int 78 main(int argc, char *argv[]) 79 { 80 int pipe_chld, pairsock[2], status; 81 struct imsgbuf ibuf; 82 struct imsg imsg; 83 ssize_t n; 84 size_t datalen; 85 86 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pairsock) == -1) 87 err(EXIT_FAILURE, "socketpair"); 88 89 pipe_chld = pairsock[1]; 90 module_bsdauth_main(pairsock[0], pairsock[1]); 91 92 /* 93 * Privileged process 94 */ 95 openlog(NULL, LOG_PID, LOG_DAEMON); 96 setproctitle("[priv]"); 97 imsg_init(&ibuf, pipe_chld); 98 99 if (pledge("stdio getpw rpath proc exec", NULL) == -1) 100 err(EXIT_FAILURE, "pledge"); 101 102 for (;;) { 103 if ((n = imsg_read(&ibuf)) <= 0 && errno != EAGAIN) 104 break; 105 for (;;) { 106 if ((n = imsg_get(&ibuf, &imsg)) == -1) 107 break; 108 if (n == 0) 109 break; 110 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 111 switch (imsg.hdr.type) { 112 case IMSG_BSDAUTH_USERCHECK: 113 { 114 char *user, *pass; 115 bool authok = false; 116 struct auth_usercheck_args 117 *args; 118 119 if (datalen < sizeof( 120 struct auth_usercheck_args)) { 121 syslog(LOG_ERR, "Short message"); 122 break; 123 } 124 args = (struct auth_usercheck_args *)imsg.data; 125 126 if (datalen < sizeof(struct auth_usercheck_args) 127 + args->userlen + args->passlen) { 128 syslog(LOG_ERR, "Short message"); 129 break; 130 } 131 user = (char *)(args + 1); 132 user[args->userlen - 1] = '\0'; 133 pass = user + args->userlen; 134 pass[args->passlen - 1] = '\0'; 135 136 if (auth_userokay(user, NULL, NULL, pass)) 137 authok = true; 138 explicit_bzero(pass, args->passlen); 139 140 imsg_compose(&ibuf, (authok) 141 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 142 0, 0, -1, NULL, 0); 143 break; 144 } 145 case IMSG_BSDAUTH_GROUPCHECK: 146 { 147 int i; 148 char *user, *group; 149 struct passwd *pw; 150 struct group gr0, *gr; 151 char g_buf[4096]; 152 bool group_ok = false; 153 struct auth_groupcheck_args 154 *args; 155 156 if (datalen < sizeof( 157 struct auth_groupcheck_args)) { 158 syslog(LOG_ERR, "Short message"); 159 break; 160 } 161 args = (struct auth_groupcheck_args *)imsg.data; 162 if (datalen < sizeof( 163 struct auth_groupcheck_args) + 164 args->userlen + args->grouplen) { 165 syslog(LOG_ERR, "Short message"); 166 break; 167 } 168 user = (char *)(args + 1); 169 user[args->userlen - 1] = '\0'; 170 group = user + args->userlen; 171 group[args->grouplen - 1] = '\0'; 172 173 pw = getpwnam(user); 174 if (getgrnam_r(group, &gr0, g_buf, 175 sizeof(g_buf), &gr) == -1 || gr == NULL) 176 goto group_done; 177 178 if (gr->gr_gid == pw->pw_gid) { 179 group_ok = true; 180 goto group_done; 181 } 182 for (i = 0; gr->gr_mem[i] != NULL; i++) { 183 if (strcmp(gr->gr_mem[i], pw->pw_name) 184 == 0) { 185 group_ok = true; 186 goto group_done; 187 } 188 } 189 group_done: 190 endgrent(); 191 192 imsg_compose(&ibuf, (group_ok) 193 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 194 0, 0, -1, NULL, 0); 195 break; 196 } 197 imsg_free(&imsg); 198 } 199 imsg_flush(&ibuf); 200 } 201 imsg_flush(&ibuf); 202 } 203 imsg_clear(&ibuf); 204 wait(&status); 205 206 exit(WEXITSTATUS(status)); 207 } 208 209 static void 210 module_bsdauth_main(int pipe_prnt, int pipe_chld) 211 { 212 int i; 213 pid_t pid; 214 struct module_bsdauth module_bsdauth; 215 216 pid = fork(); 217 if (pid == -1) 218 err(EXIT_FAILURE, "fork"); 219 220 if (pid > 0) { 221 close(pipe_prnt); 222 return; 223 } 224 close(pipe_chld); 225 226 /* main process */ 227 setproctitle("[main]"); 228 openlog(NULL, LOG_PID, LOG_DAEMON); 229 memset(&module_bsdauth, 0, sizeof(module_bsdauth)); 230 if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth, 231 &module_bsdauth_handlers)) == NULL) 232 err(1, "Could not create a module instance"); 233 234 module_drop_privilege(module_bsdauth.base); 235 236 module_load(module_bsdauth.base); 237 imsg_init(&module_bsdauth.ibuf, pipe_prnt); 238 239 if (pledge("stdio proc", NULL) == -1) 240 err(EXIT_FAILURE, "pledge"); 241 242 while (module_run(module_bsdauth.base) == 0) 243 ; 244 245 module_destroy(module_bsdauth.base); 246 imsg_clear(&module_bsdauth.ibuf); 247 248 if (module_bsdauth.okgroups) { 249 for (i = 0; module_bsdauth.okgroups[i] != NULL; i++) 250 free(module_bsdauth.okgroups[i]); 251 } 252 free(module_bsdauth.okgroups); 253 254 _exit(EXIT_SUCCESS); 255 } 256 257 static void 258 module_bsdauth_config_set(void *ctx, const char *name, int argc, 259 char * const * argv) 260 { 261 struct module_bsdauth *module = ctx; 262 int i; 263 char **groups = NULL; 264 265 if (strcmp(name, "restrict-group") == 0) { 266 if (module->okgroups != NULL) { 267 module_send_message(module->base, IMSG_NG, 268 "`restrict-group' is already defined"); 269 goto on_error; 270 } 271 if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) { 272 module_send_message(module->base, IMSG_NG, 273 "Out of memory"); 274 goto on_error; 275 } 276 for (i = 0; i < argc; i++) { 277 if ((groups[i] = strdup(argv[i])) == NULL) { 278 module_send_message(module->base, 279 IMSG_NG, "Out of memory"); 280 goto on_error; 281 } 282 } 283 groups[i] = NULL; 284 module->okgroups = groups; 285 module_send_message(module->base, IMSG_OK, NULL); 286 } else 287 module_send_message(module->base, IMSG_NG, 288 "Unknown config parameter `%s'", name); 289 return; 290 on_error: 291 if (groups != NULL) { 292 for (i = 0; groups[i] != NULL; i++) 293 free(groups[i]); 294 free(groups); 295 } 296 return; 297 } 298 299 300 static void 301 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user, 302 const char *pass) 303 { 304 struct module_bsdauth *module = ctx; 305 struct auth_usercheck_args 306 usercheck; 307 struct auth_groupcheck_args 308 groupcheck; 309 struct iovec iov[4]; 310 const char *group; 311 u_int i; 312 const char *reason; 313 struct imsg imsg; 314 ssize_t n; 315 316 memset(&imsg, 0, sizeof(imsg)); 317 if (pass == NULL) 318 pass = ""; 319 320 usercheck.userlen = strlen(user) + 1; 321 usercheck.passlen = strlen(pass) + 1; 322 iov[0].iov_base = &usercheck; 323 iov[0].iov_len = sizeof(usercheck); 324 iov[1].iov_base = (char *)user; 325 iov[1].iov_len = usercheck.userlen; 326 iov[2].iov_base = (char *)pass; 327 iov[2].iov_len = usercheck.passlen; 328 329 imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3); 330 imsg_flush(&module->ibuf); 331 if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) 332 fatal("imsg_read() failed in module_bsdauth_userpass()"); 333 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 334 fatal("imsg_get() failed in module_bsdauth_userpass()"); 335 336 if (imsg.hdr.type != IMSG_BSDAUTH_OK) { 337 reason = "Authentication failed"; 338 goto auth_ng; 339 } 340 if (module->okgroups != NULL) { 341 reason = "Group restriction is not allowed"; 342 for (i = 0; module->okgroups[i] != NULL; i++) { 343 group = module->okgroups[i]; 344 345 groupcheck.userlen = strlen(user) + 1; 346 groupcheck.grouplen = strlen(group) + 1; 347 iov[0].iov_base = &groupcheck; 348 iov[0].iov_len = sizeof(groupcheck); 349 iov[1].iov_base = (char *)user; 350 iov[1].iov_len = groupcheck.userlen; 351 iov[2].iov_base = (char *)group; 352 iov[2].iov_len = groupcheck.grouplen; 353 imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK, 354 0, 0, -1, iov, 3); 355 imsg_flush(&module->ibuf); 356 if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) 357 fatal("imsg_read() failed in " 358 "module_bsdauth_userpass()"); 359 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 360 fatal("imsg_get() failed in " 361 "module_bsdauth_userpass()"); 362 if (imsg.hdr.type == IMSG_BSDAUTH_OK) 363 goto group_ok; 364 } 365 goto auth_ng; 366 } 367 group_ok: 368 module_userpass_ok(module->base, q_id, "Authentication succeeded"); 369 imsg_free(&imsg); 370 return; 371 auth_ng: 372 module_userpass_fail(module->base, q_id, reason); 373 imsg_free(&imsg); 374 return; 375 } 376 377 static void 378 fatal(const char *msg) 379 { 380 syslog(LOG_ERR, "%s: %m", msg); 381 abort(); 382 } 383