1 /* $OpenBSD: radiusd_bsdauth.c,v 1.19 2024/11/21 13:43:10 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 <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 if (imsgbuf_init(&ibuf, pairsock[0]) == 1) 117 err(EXIT_FAILURE, "imsgbuf_init"); 118 119 if (pledge("stdio getpw rpath proc exec", NULL) == -1) 120 err(EXIT_FAILURE, "pledge"); 121 122 for (;;) { 123 if (imsgbuf_read(&ibuf) != 1) 124 break; 125 for (;;) { 126 if ((n = imsg_get(&ibuf, &imsg)) == -1) 127 break; 128 if (n == 0) 129 break; 130 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 131 switch (imsg.hdr.type) { 132 case IMSG_BSDAUTH_USERCHECK: 133 { 134 char *user, *pass; 135 bool authok = false; 136 struct auth_usercheck_args 137 *args; 138 139 if (datalen < sizeof( 140 struct auth_usercheck_args)) { 141 syslog(LOG_ERR, "Short message"); 142 break; 143 } 144 args = (struct auth_usercheck_args *)imsg.data; 145 146 if (datalen < sizeof(struct auth_usercheck_args) 147 + args->userlen + args->passlen) { 148 syslog(LOG_ERR, "Short message"); 149 break; 150 } 151 user = (char *)(args + 1); 152 user[args->userlen - 1] = '\0'; 153 pass = user + args->userlen; 154 pass[args->passlen - 1] = '\0'; 155 156 if (auth_userokay(user, NULL, NULL, pass)) 157 authok = true; 158 explicit_bzero(pass, args->passlen); 159 160 imsg_compose(&ibuf, (authok) 161 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 162 0, 0, -1, NULL, 0); 163 break; 164 } 165 case IMSG_BSDAUTH_GROUPCHECK: 166 { 167 int i; 168 char *user, *group; 169 struct passwd *pw; 170 struct group gr0, *gr; 171 char g_buf[4096]; 172 bool group_ok = false; 173 struct auth_groupcheck_args 174 *args; 175 176 if (datalen < sizeof( 177 struct auth_groupcheck_args)) { 178 syslog(LOG_ERR, "Short message"); 179 break; 180 } 181 args = (struct auth_groupcheck_args *)imsg.data; 182 if (datalen < 183 sizeof(struct auth_groupcheck_args) + 184 args->userlen + args->grouplen) { 185 syslog(LOG_ERR, "Short message"); 186 break; 187 } 188 user = (char *)(args + 1); 189 user[args->userlen - 1] = '\0'; 190 group = user + args->userlen; 191 group[args->grouplen - 1] = '\0'; 192 193 user[strcspn(user, ":")] = '\0'; 194 pw = getpwnam(user); 195 if (pw == NULL) 196 goto invalid; 197 if (getgrnam_r(group, &gr0, g_buf, 198 sizeof(g_buf), &gr) == -1 || gr == NULL) 199 goto invalid; 200 201 if (gr->gr_gid == pw->pw_gid) { 202 group_ok = true; 203 goto invalid; 204 } 205 for (i = 0; gr->gr_mem[i] != NULL; i++) { 206 if (strcmp(gr->gr_mem[i], pw->pw_name) 207 == 0) { 208 group_ok = true; 209 goto invalid; 210 } 211 } 212 invalid: 213 endgrent(); 214 215 imsg_compose(&ibuf, (group_ok) 216 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 217 0, 0, -1, NULL, 0); 218 break; 219 } 220 } 221 imsg_free(&imsg); 222 imsgbuf_flush(&ibuf); 223 } 224 imsgbuf_flush(&ibuf); 225 } 226 imsgbuf_clear(&ibuf); 227 228 while (waitpid(pid, &status, 0) == -1) { 229 if (errno != EINTR) 230 break; 231 } 232 exit(WEXITSTATUS(status)); 233 } 234 235 static void 236 module_bsdauth_main(void) 237 { 238 int i; 239 struct module_bsdauth module_bsdauth; 240 241 /* 242 * main process 243 */ 244 setproctitle("[main]"); 245 openlog(NULL, LOG_PID, LOG_DAEMON); 246 memset(&module_bsdauth, 0, sizeof(module_bsdauth)); 247 if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth, 248 &module_bsdauth_handlers)) == NULL) 249 err(1, "Could not create a module instance"); 250 251 module_drop_privilege(module_bsdauth.base, 0); 252 253 module_load(module_bsdauth.base); 254 if (imsgbuf_init(&module_bsdauth.ibuf, 3) == -1) 255 err(EXIT_FAILURE, "imsgbuf_init"); 256 257 if (pledge("stdio proc", NULL) == -1) 258 err(EXIT_FAILURE, "pledge"); 259 260 while (module_run(module_bsdauth.base) == 0) 261 ; 262 263 module_destroy(module_bsdauth.base); 264 imsgbuf_clear(&module_bsdauth.ibuf); 265 266 if (module_bsdauth.okgroups) { 267 for (i = 0; module_bsdauth.okgroups[i] != NULL; i++) 268 free(module_bsdauth.okgroups[i]); 269 } 270 free(module_bsdauth.okgroups); 271 272 exit(EXIT_SUCCESS); 273 } 274 275 static void 276 module_bsdauth_config_set(void *ctx, const char *name, int argc, 277 char * const * argv) 278 { 279 struct module_bsdauth *module = ctx; 280 int i; 281 char **groups = NULL; 282 283 if (strcmp(name, "restrict-group") == 0) { 284 if (module->okgroups != NULL) { 285 module_send_message(module->base, IMSG_NG, 286 "`restrict-group' is already defined"); 287 goto on_error; 288 } 289 if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) { 290 module_send_message(module->base, IMSG_NG, 291 "Out of memory"); 292 goto on_error; 293 } 294 for (i = 0; i < argc; i++) { 295 if ((groups[i] = strdup(argv[i])) == NULL) { 296 module_send_message(module->base, 297 IMSG_NG, "Out of memory"); 298 goto on_error; 299 } 300 } 301 groups[i] = NULL; 302 module->okgroups = groups; 303 module_send_message(module->base, IMSG_OK, NULL); 304 } else if (strncmp(name, "_", 1) == 0) 305 /* ignore all internal messages */ 306 module_send_message(module->base, IMSG_OK, NULL); 307 else 308 module_send_message(module->base, IMSG_NG, 309 "Unknown config parameter `%s'", name); 310 return; 311 on_error: 312 if (groups != NULL) { 313 for (i = 0; groups[i] != NULL; i++) 314 free(groups[i]); 315 free(groups); 316 } 317 return; 318 } 319 320 321 static void 322 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user, 323 const char *pass) 324 { 325 struct module_bsdauth *module = ctx; 326 struct auth_usercheck_args 327 usercheck; 328 struct auth_groupcheck_args 329 groupcheck; 330 struct iovec iov[4]; 331 const char *group; 332 u_int i; 333 const char *reason; 334 struct imsg imsg; 335 ssize_t n; 336 337 memset(&imsg, 0, sizeof(imsg)); 338 if (pass == NULL) 339 pass = ""; 340 341 usercheck.userlen = strlen(user) + 1; 342 usercheck.passlen = strlen(pass) + 1; 343 iov[0].iov_base = &usercheck; 344 iov[0].iov_len = sizeof(usercheck); 345 iov[1].iov_base = (char *)user; 346 iov[1].iov_len = usercheck.userlen; 347 iov[2].iov_base = (char *)pass; 348 iov[2].iov_len = usercheck.passlen; 349 350 imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3); 351 imsgbuf_flush(&module->ibuf); 352 if (imsgbuf_read(&module->ibuf) != 1) 353 fatal("imsgbuf_read() failed in module_bsdauth_userpass()"); 354 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 355 fatal("imsg_get() failed in module_bsdauth_userpass()"); 356 357 if (imsg.hdr.type != IMSG_BSDAUTH_OK) { 358 reason = "Authentication failed"; 359 goto auth_ng; 360 } 361 if (module->okgroups != NULL) { 362 reason = "Group restriction is not allowed"; 363 for (i = 0; module->okgroups[i] != NULL; i++) { 364 group = module->okgroups[i]; 365 366 groupcheck.userlen = strlen(user) + 1; 367 groupcheck.grouplen = strlen(group) + 1; 368 iov[0].iov_base = &groupcheck; 369 iov[0].iov_len = sizeof(groupcheck); 370 iov[1].iov_base = (char *)user; 371 iov[1].iov_len = groupcheck.userlen; 372 iov[2].iov_base = (char *)group; 373 iov[2].iov_len = groupcheck.grouplen; 374 imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK, 375 0, 0, -1, iov, 3); 376 imsgbuf_flush(&module->ibuf); 377 if (imsgbuf_read(&module->ibuf) != 1) 378 fatal("imsgbuf_read() failed in " 379 "module_bsdauth_userpass()"); 380 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 381 fatal("imsg_get() failed in " 382 "module_bsdauth_userpass()"); 383 if (imsg.hdr.type == IMSG_BSDAUTH_OK) 384 goto group_ok; 385 } 386 goto auth_ng; 387 } 388 group_ok: 389 module_userpass_ok(module->base, q_id, "Authentication succeeded"); 390 imsg_free(&imsg); 391 return; 392 auth_ng: 393 module_userpass_fail(module->base, q_id, reason); 394 imsg_free(&imsg); 395 return; 396 } 397 398 pid_t 399 start_child(char *argv0, int fd) 400 { 401 char *argv[5]; 402 int argc = 0; 403 pid_t pid; 404 405 switch (pid = fork()) { 406 case -1: 407 fatal("cannot fork"); 408 case 0: 409 break; 410 default: 411 close(fd); 412 return (pid); 413 } 414 415 if (fd != 3) { 416 if (dup2(fd, 3) == -1) 417 fatal("cannot setup imsg fd"); 418 } else if (fcntl(fd, F_SETFD, 0) == -1) 419 fatal("cannot setup imsg fd"); 420 421 argv[argc++] = argv0; 422 argv[argc++] = "-M"; /* main proc */ 423 argv[argc++] = NULL; 424 execvp(argv0, argv); 425 fatal("execvp"); 426 } 427 428 static void 429 fatal(const char *msg) 430 { 431 syslog(LOG_ERR, "%s: %m", msg); 432 abort(); 433 } 434