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