1 /* $OpenBSD: radiusd_bsdauth.c,v 1.8 2017/08/21 21:41:13 deraadt 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 pid_t 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 pid_t pid; 86 87 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pairsock) == -1) 88 err(EXIT_FAILURE, "socketpair"); 89 90 pipe_chld = pairsock[1]; 91 pid = module_bsdauth_main(pairsock[0], pairsock[1]); 92 93 /* 94 * Privileged process 95 */ 96 openlog(NULL, LOG_PID, LOG_DAEMON); 97 setproctitle("[priv]"); 98 imsg_init(&ibuf, pipe_chld); 99 100 if (pledge("stdio getpw rpath proc exec", NULL) == -1) 101 err(EXIT_FAILURE, "pledge"); 102 103 for (;;) { 104 if ((n = imsg_read(&ibuf)) <= 0 && errno != EAGAIN) 105 break; 106 for (;;) { 107 if ((n = imsg_get(&ibuf, &imsg)) == -1) 108 break; 109 if (n == 0) 110 break; 111 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 112 switch (imsg.hdr.type) { 113 case IMSG_BSDAUTH_USERCHECK: 114 { 115 char *user, *pass; 116 bool authok = false; 117 struct auth_usercheck_args 118 *args; 119 120 if (datalen < sizeof( 121 struct auth_usercheck_args)) { 122 syslog(LOG_ERR, "Short message"); 123 break; 124 } 125 args = (struct auth_usercheck_args *)imsg.data; 126 127 if (datalen < sizeof(struct auth_usercheck_args) 128 + args->userlen + args->passlen) { 129 syslog(LOG_ERR, "Short message"); 130 break; 131 } 132 user = (char *)(args + 1); 133 user[args->userlen - 1] = '\0'; 134 pass = user + args->userlen; 135 pass[args->passlen - 1] = '\0'; 136 137 if (auth_userokay(user, NULL, NULL, pass)) 138 authok = true; 139 explicit_bzero(pass, args->passlen); 140 141 imsg_compose(&ibuf, (authok) 142 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 143 0, 0, -1, NULL, 0); 144 break; 145 } 146 case IMSG_BSDAUTH_GROUPCHECK: 147 { 148 int i; 149 char *user, *group; 150 struct passwd *pw; 151 struct group gr0, *gr; 152 char g_buf[4096]; 153 bool group_ok = false; 154 struct auth_groupcheck_args 155 *args; 156 157 if (datalen < sizeof( 158 struct auth_groupcheck_args)) { 159 syslog(LOG_ERR, "Short message"); 160 break; 161 } 162 args = (struct auth_groupcheck_args *)imsg.data; 163 if (datalen < sizeof( 164 struct auth_groupcheck_args) + 165 args->userlen + args->grouplen) { 166 syslog(LOG_ERR, "Short message"); 167 break; 168 } 169 user = (char *)(args + 1); 170 user[args->userlen - 1] = '\0'; 171 group = user + args->userlen; 172 group[args->grouplen - 1] = '\0'; 173 174 pw = getpwnam(user); 175 if (getgrnam_r(group, &gr0, g_buf, 176 sizeof(g_buf), &gr) == -1 || gr == NULL) 177 goto group_done; 178 179 if (gr->gr_gid == pw->pw_gid) { 180 group_ok = true; 181 goto group_done; 182 } 183 for (i = 0; gr->gr_mem[i] != NULL; i++) { 184 if (strcmp(gr->gr_mem[i], pw->pw_name) 185 == 0) { 186 group_ok = true; 187 goto group_done; 188 } 189 } 190 group_done: 191 endgrent(); 192 193 imsg_compose(&ibuf, (group_ok) 194 ? IMSG_BSDAUTH_OK : IMSG_BSDAUTH_NG, 195 0, 0, -1, NULL, 0); 196 break; 197 } 198 imsg_free(&imsg); 199 } 200 imsg_flush(&ibuf); 201 } 202 imsg_flush(&ibuf); 203 } 204 imsg_clear(&ibuf); 205 206 while (waitpid(pid, &status, 0) == -1) { 207 if (errno != EINTR) 208 break; 209 } 210 exit(WEXITSTATUS(status)); 211 } 212 213 static pid_t 214 module_bsdauth_main(int pipe_prnt, int pipe_chld) 215 { 216 int i; 217 pid_t pid; 218 struct module_bsdauth module_bsdauth; 219 220 pid = fork(); 221 if (pid == -1) 222 err(EXIT_FAILURE, "fork"); 223 224 if (pid > 0) { 225 close(pipe_prnt); 226 return (pid); 227 } 228 close(pipe_chld); 229 230 /* main process */ 231 setproctitle("[main]"); 232 openlog(NULL, LOG_PID, LOG_DAEMON); 233 memset(&module_bsdauth, 0, sizeof(module_bsdauth)); 234 if ((module_bsdauth.base = module_create(STDIN_FILENO, &module_bsdauth, 235 &module_bsdauth_handlers)) == NULL) 236 err(1, "Could not create a module instance"); 237 238 module_drop_privilege(module_bsdauth.base); 239 240 module_load(module_bsdauth.base); 241 imsg_init(&module_bsdauth.ibuf, pipe_prnt); 242 243 if (pledge("stdio proc", NULL) == -1) 244 err(EXIT_FAILURE, "pledge"); 245 246 while (module_run(module_bsdauth.base) == 0) 247 ; 248 249 module_destroy(module_bsdauth.base); 250 imsg_clear(&module_bsdauth.ibuf); 251 252 if (module_bsdauth.okgroups) { 253 for (i = 0; module_bsdauth.okgroups[i] != NULL; i++) 254 free(module_bsdauth.okgroups[i]); 255 } 256 free(module_bsdauth.okgroups); 257 258 _exit(EXIT_SUCCESS); 259 } 260 261 static void 262 module_bsdauth_config_set(void *ctx, const char *name, int argc, 263 char * const * argv) 264 { 265 struct module_bsdauth *module = ctx; 266 int i; 267 char **groups = NULL; 268 269 if (strcmp(name, "restrict-group") == 0) { 270 if (module->okgroups != NULL) { 271 module_send_message(module->base, IMSG_NG, 272 "`restrict-group' is already defined"); 273 goto on_error; 274 } 275 if ((groups = calloc(sizeof(char *), argc + 1)) == NULL) { 276 module_send_message(module->base, IMSG_NG, 277 "Out of memory"); 278 goto on_error; 279 } 280 for (i = 0; i < argc; i++) { 281 if ((groups[i] = strdup(argv[i])) == NULL) { 282 module_send_message(module->base, 283 IMSG_NG, "Out of memory"); 284 goto on_error; 285 } 286 } 287 groups[i] = NULL; 288 module->okgroups = groups; 289 module_send_message(module->base, IMSG_OK, NULL); 290 } else 291 module_send_message(module->base, IMSG_NG, 292 "Unknown config parameter `%s'", name); 293 return; 294 on_error: 295 if (groups != NULL) { 296 for (i = 0; groups[i] != NULL; i++) 297 free(groups[i]); 298 free(groups); 299 } 300 return; 301 } 302 303 304 static void 305 module_bsdauth_userpass(void *ctx, u_int q_id, const char *user, 306 const char *pass) 307 { 308 struct module_bsdauth *module = ctx; 309 struct auth_usercheck_args 310 usercheck; 311 struct auth_groupcheck_args 312 groupcheck; 313 struct iovec iov[4]; 314 const char *group; 315 u_int i; 316 const char *reason; 317 struct imsg imsg; 318 ssize_t n; 319 320 memset(&imsg, 0, sizeof(imsg)); 321 if (pass == NULL) 322 pass = ""; 323 324 usercheck.userlen = strlen(user) + 1; 325 usercheck.passlen = strlen(pass) + 1; 326 iov[0].iov_base = &usercheck; 327 iov[0].iov_len = sizeof(usercheck); 328 iov[1].iov_base = (char *)user; 329 iov[1].iov_len = usercheck.userlen; 330 iov[2].iov_base = (char *)pass; 331 iov[2].iov_len = usercheck.passlen; 332 333 imsg_composev(&module->ibuf, IMSG_BSDAUTH_USERCHECK, 0, 0, -1, iov, 3); 334 imsg_flush(&module->ibuf); 335 if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) 336 fatal("imsg_read() failed in module_bsdauth_userpass()"); 337 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 338 fatal("imsg_get() failed in module_bsdauth_userpass()"); 339 340 if (imsg.hdr.type != IMSG_BSDAUTH_OK) { 341 reason = "Authentication failed"; 342 goto auth_ng; 343 } 344 if (module->okgroups != NULL) { 345 reason = "Group restriction is not allowed"; 346 for (i = 0; module->okgroups[i] != NULL; i++) { 347 group = module->okgroups[i]; 348 349 groupcheck.userlen = strlen(user) + 1; 350 groupcheck.grouplen = strlen(group) + 1; 351 iov[0].iov_base = &groupcheck; 352 iov[0].iov_len = sizeof(groupcheck); 353 iov[1].iov_base = (char *)user; 354 iov[1].iov_len = groupcheck.userlen; 355 iov[2].iov_base = (char *)group; 356 iov[2].iov_len = groupcheck.grouplen; 357 imsg_composev(&module->ibuf, IMSG_BSDAUTH_GROUPCHECK, 358 0, 0, -1, iov, 3); 359 imsg_flush(&module->ibuf); 360 if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) 361 fatal("imsg_read() failed in " 362 "module_bsdauth_userpass()"); 363 if ((n = imsg_get(&module->ibuf, &imsg)) <= 0) 364 fatal("imsg_get() failed in " 365 "module_bsdauth_userpass()"); 366 if (imsg.hdr.type == IMSG_BSDAUTH_OK) 367 goto group_ok; 368 } 369 goto auth_ng; 370 } 371 group_ok: 372 module_userpass_ok(module->base, q_id, "Authentication succeeded"); 373 imsg_free(&imsg); 374 return; 375 auth_ng: 376 module_userpass_fail(module->base, q_id, reason); 377 imsg_free(&imsg); 378 return; 379 } 380 381 static void 382 fatal(const char *msg) 383 { 384 syslog(LOG_ERR, "%s: %m", msg); 385 abort(); 386 } 387