1 /* $OpenBSD: radiusd_bsdauth.c,v 1.12 2019/04/01 11:05:41 yasuoka 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 <fcntl.h> 29 #include <grp.h> 30 #include <imsg.h> 31 #include <login_cap.h> 32 #include <pwd.h> 33 #include <stdbool.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <syslog.h> 38 #include <unistd.h> 39 40 #include "radiusd.h" 41 #include "radiusd_module.h" 42 43 struct module_bsdauth { 44 struct module_base *base; 45 struct imsgbuf ibuf; 46 char **okgroups; 47 }; 48 49 /* IPC between priv and main */ 50 enum { 51 IMSG_BSDAUTH_OK = 1000, 52 IMSG_BSDAUTH_NG, 53 IMSG_BSDAUTH_USERCHECK, 54 IMSG_BSDAUTH_GROUPCHECK 55 }; 56 struct auth_usercheck_args { 57 size_t userlen; 58 size_t passlen; 59 }; 60 struct auth_groupcheck_args { 61 size_t userlen; 62 size_t grouplen; 63 }; 64 65 __dead static void 66 module_bsdauth_main(void); 67 static void module_bsdauth_config_set(void *, const char *, int, 68 char * const *); 69 static void module_bsdauth_userpass(void *, u_int, const char *, 70 const char *); 71 static pid_t start_child(char *, int); 72 __dead static void 73 fatal(const char *); 74 75 static struct module_handlers module_bsdauth_handlers = { 76 .userpass = module_bsdauth_userpass, 77 .config_set = module_bsdauth_config_set 78 }; 79 80 int 81 main(int argc, char *argv[]) 82 { 83 int ch, pairsock[2], status; 84 struct imsgbuf ibuf; 85 struct imsg imsg; 86 ssize_t n; 87 size_t datalen; 88 pid_t pid; 89 char *saved_argv0; 90 91 while ((ch = getopt(argc, argv, "M")) != -1) 92 switch (ch) { 93 case 'M': 94 module_bsdauth_main(); 95 /* never return, not rearched here */ 96 break; 97 default: 98 break; 99 } 100 saved_argv0 = argv[0]; 101 argc -= optind; 102 argv += optind; 103 104 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC, 105 pairsock) == -1) 106 err(EXIT_FAILURE, "socketpair"); 107 108 openlog(NULL, LOG_PID, LOG_DAEMON); 109 110 pid = start_child(saved_argv0, pairsock[1]); 111 112 /* 113 * Privileged process 114 */ 115 setproctitle("[priv]"); 116 imsg_init(&ibuf, pairsock[0]); 117 118 if (pledge("stdio getpw rpath proc exec", NULL) == -1) 119 err(EXIT_FAILURE, "pledge"); 120 121 for (;;) { 122 if ((n = imsg_read(&ibuf)) <= 0 && errno != EAGAIN) 123 break; 124 for (;;) { 125 if ((n = imsg_get(&ibuf, &imsg)) == -1) 126 break; 127 if (n == 0) 128 break; 129 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 130 switch (imsg.hdr.type) { 131 case IMSG_BSDAUTH_USERCHECK: 132 { 133 char *user, *pass; 134 bool authok = false; 135 struct auth_usercheck_args 136 *args; 137 138 if (datalen < sizeof( 139 struct auth_usercheck_args)) { 140 syslog(LOG_ERR, "Short message"); 141 break; 142 } 143 args = (struct auth_usercheck_args *)imsg.data; 144 145 if (datalen < sizeof(struct auth_usercheck_args) 146 + args->userlen + args->passlen) { 147 syslog(LOG_ERR, "Short message"); 148 break; 149 } 150 user = (char *)(args + 1); 151 user[args->userlen - 1] = '\0'; 152 pass = user + args->userlen; 153 pass[args->passlen - 1] = '\0'; 154 155 if (auth_userokay(user, NULL, NULL, pass)) 156 authok = true; 157 explicit_bzero(pass, args->passlen); 158 159 imsg_compose(&ibuf, (authok) 160 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 161 0, 0, -1, NULL, 0); 162 break; 163 } 164 case IMSG_BSDAUTH_GROUPCHECK: 165 { 166 int i; 167 char *user, *group; 168 struct passwd *pw; 169 struct group gr0, *gr; 170 char g_buf[4096]; 171 bool group_ok = false; 172 struct auth_groupcheck_args 173 *args; 174 175 if (datalen < sizeof( 176 struct auth_groupcheck_args)) { 177 syslog(LOG_ERR, "Short message"); 178 break; 179 } 180 args = (struct auth_groupcheck_args *)imsg.data; 181 if (datalen < 182 sizeof(struct auth_groupcheck_args) + 183 args->userlen + args->grouplen) { 184 syslog(LOG_ERR, "Short message"); 185 break; 186 } 187 user = (char *)(args + 1); 188 user[args->userlen - 1] = '\0'; 189 group = user + args->userlen; 190 group[args->grouplen - 1] = '\0'; 191 192 pw = getpwnam(user); 193 if (getgrnam_r(group, &gr0, g_buf, 194 sizeof(g_buf), &gr) == -1 || gr == NULL) 195 goto group_done; 196 197 if (gr->gr_gid == pw->pw_gid) { 198 group_ok = true; 199 goto group_done; 200 } 201 for (i = 0; gr->gr_mem[i] != NULL; i++) { 202 if (strcmp(gr->gr_mem[i], pw->pw_name) 203 == 0) { 204 group_ok = true; 205 goto group_done; 206 } 207 } 208 group_done: 209 endgrent(); 210 211 imsg_compose(&ibuf, (group_ok) 212 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 213 0, 0, -1, NULL, 0); 214 break; 215 } 216 } 217 imsg_free(&imsg); 218 imsg_flush(&ibuf); 219 } 220 imsg_flush(&ibuf); 221 } 222 imsg_clear(&ibuf); 223 224 while (waitpid(pid, &status, 0) == -1) { 225 if (errno != EINTR) 226 break; 227 } 228 exit(WEXITSTATUS(status)); 229 } 230 231 static void 232 module_bsdauth_main(void) 233 { 234 int i; 235 struct module_bsdauth module_bsdauth; 236 237 /* 238 * main process 239 */ 240 setproctitle("[main]"); 241 openlog(NULL, LOG_PID, LOG_DAEMON); 242 memset(&module_bsdauth, 0, sizeof(module_bsdauth)); 243 if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth, 244 &module_bsdauth_handlers)) == NULL) 245 err(1, "Could not create a module instance"); 246 247 module_drop_privilege(module_bsdauth.base); 248 249 module_load(module_bsdauth.base); 250 imsg_init(&module_bsdauth.ibuf, 3); 251 252 if (pledge("stdio proc", NULL) == -1) 253 err(EXIT_FAILURE, "pledge"); 254 255 while (module_run(module_bsdauth.base) == 0) 256 ; 257 258 module_destroy(module_bsdauth.base); 259 imsg_clear(&module_bsdauth.ibuf); 260 261 if (module_bsdauth.okgroups) { 262 for (i = 0; module_bsdauth.okgroups[i] != NULL; i++) 263 free(module_bsdauth.okgroups[i]); 264 } 265 free(module_bsdauth.okgroups); 266 267 exit(EXIT_SUCCESS); 268 } 269 270 static void 271 module_bsdauth_config_set(void *ctx, const char *name, int argc, 272 char * const * argv) 273 { 274 struct module_bsdauth *module = ctx; 275 int i; 276 char **groups = NULL; 277 278 if (strcmp(name, "restrict-group") == 0) { 279 if (module->okgroups != NULL) { 280 module_send_message(module->base, IMSG_NG, 281 "`restrict-group' is already defined"); 282 goto on_error; 283 } 284 if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) { 285 module_send_message(module->base, IMSG_NG, 286 "Out of memory"); 287 goto on_error; 288 } 289 for (i = 0; i < argc; i++) { 290 if ((groups[i] = strdup(argv[i])) == NULL) { 291 module_send_message(module->base, 292 IMSG_NG, "Out of memory"); 293 goto on_error; 294 } 295 } 296 groups[i] = NULL; 297 module->okgroups = groups; 298 module_send_message(module->base, IMSG_OK, NULL); 299 } else if (strncmp(name, "_", 1) == 0) 300 /* ignore all internal messages */ 301 module_send_message(module->base, IMSG_OK, NULL); 302 else 303 module_send_message(module->base, IMSG_NG, 304 "Unknown config parameter `%s'", name); 305 return; 306 on_error: 307 if (groups != NULL) { 308 for (i = 0; groups[i] != NULL; i++) 309 free(groups[i]); 310 free(groups); 311 } 312 return; 313 } 314 315 316 static void 317 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user, 318 const char *pass) 319 { 320 struct module_bsdauth *module = ctx; 321 struct auth_usercheck_args 322 usercheck; 323 struct auth_groupcheck_args 324 groupcheck; 325 struct iovec iov[4]; 326 const char *group; 327 u_int i; 328 const char *reason; 329 struct imsg imsg; 330 ssize_t n; 331 332 memset(&imsg, 0, sizeof(imsg)); 333 if (pass == NULL) 334 pass = ""; 335 336 usercheck.userlen = strlen(user) + 1; 337 usercheck.passlen = strlen(pass) + 1; 338 iov[0].iov_base = &usercheck; 339 iov[0].iov_len = sizeof(usercheck); 340 iov[1].iov_base = (char *)user; 341 iov[1].iov_len = usercheck.userlen; 342 iov[2].iov_base = (char *)pass; 343 iov[2].iov_len = usercheck.passlen; 344 345 imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3); 346 imsg_flush(&module->ibuf); 347 if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) 348 fatal("imsg_read() failed in module_bsdauth_userpass()"); 349 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 350 fatal("imsg_get() failed in module_bsdauth_userpass()"); 351 352 if (imsg.hdr.type != IMSG_BSDAUTH_OK) { 353 reason = "Authentication failed"; 354 goto auth_ng; 355 } 356 if (module->okgroups != NULL) { 357 reason = "Group restriction is not allowed"; 358 for (i = 0; module->okgroups[i] != NULL; i++) { 359 group = module->okgroups[i]; 360 361 groupcheck.userlen = strlen(user) + 1; 362 groupcheck.grouplen = strlen(group) + 1; 363 iov[0].iov_base = &groupcheck; 364 iov[0].iov_len = sizeof(groupcheck); 365 iov[1].iov_base = (char *)user; 366 iov[1].iov_len = groupcheck.userlen; 367 iov[2].iov_base = (char *)group; 368 iov[2].iov_len = groupcheck.grouplen; 369 imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK, 370 0, 0, -1, iov, 3); 371 imsg_flush(&module->ibuf); 372 if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) 373 fatal("imsg_read() failed in " 374 "module_bsdauth_userpass()"); 375 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 376 fatal("imsg_get() failed in " 377 "module_bsdauth_userpass()"); 378 if (imsg.hdr.type == IMSG_BSDAUTH_OK) 379 goto group_ok; 380 } 381 goto auth_ng; 382 } 383 group_ok: 384 module_userpass_ok(module->base, q_id, "Authentication succeeded"); 385 imsg_free(&imsg); 386 return; 387 auth_ng: 388 module_userpass_fail(module->base, q_id, reason); 389 imsg_free(&imsg); 390 return; 391 } 392 393 pid_t 394 start_child(char *argv0, int fd) 395 { 396 char *argv[5]; 397 int argc = 0; 398 pid_t pid; 399 400 switch (pid = fork()) { 401 case -1: 402 fatal("cannot fork"); 403 case 0: 404 break; 405 default: 406 close(fd); 407 return (pid); 408 } 409 410 if (fd != 3) { 411 if (dup2(fd, 3) == -1) 412 fatal("cannot setup imsg fd"); 413 } else if (fcntl(fd, F_SETFD, 0) == -1) 414 fatal("cannot setup imsg fd"); 415 416 argv[argc++] = argv0; 417 argv[argc++] = "-M"; /* main proc */ 418 execvp(argv0, argv); 419 fatal("execvp"); 420 } 421 422 static void 423 fatal(const char *msg) 424 { 425 syslog(LOG_ERR, "%s: %m", msg); 426 abort(); 427 } 428