1 /* $OpenBSD: ypldap.c,v 1.7 2009/01/29 11:21:42 form Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 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/param.h> 21 #include <sys/queue.h> 22 #include <sys/socket.h> 23 #include <sys/tree.h> 24 #include <sys/wait.h> 25 26 #include <netinet/in.h> 27 #include <arpa/inet.h> 28 29 #include <err.h> 30 #include <event.h> 31 #include <fcntl.h> 32 #include <unistd.h> 33 #include <pwd.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 38 #include "ypldap.h" 39 40 __dead void usage(void); 41 int check_child(pid_t, const char *); 42 void main_sig_handler(int, short, void *); 43 void main_shutdown(void); 44 void main_dispatch_client(int, short, void *); 45 void main_configure_client(struct env *); 46 void main_init_timer(int, short, void *); 47 void purge_config(struct env *); 48 void reconfigure(struct env *); 49 void set_nonblock(int); 50 51 int pipe_main2client[2]; 52 53 pid_t client_pid = 0; 54 char *conffile = YPLDAP_CONF_FILE; 55 int opts = 0; 56 57 void 58 usage(void) 59 { 60 extern const char *__progname; 61 62 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", 63 __progname); 64 exit(1); 65 } 66 67 int 68 check_child(pid_t pid, const char *pname) 69 { 70 int status; 71 72 if (waitpid(pid, &status, WNOHANG) > 0) { 73 if (WIFEXITED(status)) { 74 log_warnx("check_child: lost child %s exited", pname); 75 return (1); 76 } 77 if (WIFSIGNALED(status)) { 78 log_warnx("check_child: lost child %s terminated; " 79 "signal %d", pname, WTERMSIG(status)); 80 return (1); 81 } 82 } 83 return (0); 84 } 85 86 /* ARGUSED */ 87 void 88 main_sig_handler(int sig, short event, void *p) 89 { 90 int die = 0; 91 92 switch (sig) { 93 case SIGTERM: 94 case SIGINT: 95 die = 1; 96 /* FALLTHROUGH */ 97 case SIGCHLD: 98 if (check_child(client_pid, "ldap client")) { 99 client_pid = 0; 100 die = 1; 101 } 102 if (die) 103 main_shutdown(); 104 break; 105 case SIGHUP: 106 /* reconfigure */ 107 break; 108 default: 109 fatalx("unexpected signal"); 110 } 111 } 112 113 void 114 main_shutdown(void) 115 { 116 _exit(0); 117 } 118 119 void 120 main_dispatch_client(int fd, short event, void *p) 121 { 122 int n; 123 int shut = 0; 124 struct env *env = p; 125 struct imsgbuf *ibuf = env->sc_ibuf; 126 struct idm_req ir; 127 struct imsg imsg; 128 129 switch (event) { 130 case EV_READ: 131 if ((n = imsg_read(ibuf)) == -1) 132 fatal("imsg_read error"); 133 if (n == 0) 134 shut = 1; 135 break; 136 case EV_WRITE: 137 if (msgbuf_write(&ibuf->w) == -1) 138 fatal("msgbuf_write"); 139 imsg_event_add(ibuf); 140 return; 141 default: 142 fatalx("unknown event"); 143 } 144 145 for (;;) { 146 if ((n = imsg_get(ibuf, &imsg)) == -1) 147 fatal("main_dispatch_client: imsg_read error"); 148 if (n == 0) 149 break; 150 151 switch (imsg.hdr.type) { 152 case IMSG_START_UPDATE: 153 log_debug("starting directory update"); 154 env->sc_user_line_len = 0; 155 env->sc_group_line_len = 0; 156 if ((env->sc_user_names_t = calloc(1, 157 sizeof(*env->sc_user_names_t))) == NULL || 158 (env->sc_group_names_t = calloc(1, 159 sizeof(*env->sc_group_names_t))) == NULL) 160 fatal(NULL); 161 RB_INIT(env->sc_user_names_t); 162 RB_INIT(env->sc_group_names_t); 163 break; 164 case IMSG_PW_ENTRY: { 165 struct userent *ue; 166 size_t len; 167 168 (void)memcpy(&ir, imsg.data, sizeof(ir)); 169 if ((ue = calloc(1, sizeof(*ue))) == NULL || 170 (ue->ue_line = strdup(ir.ir_line)) == NULL) { 171 /* 172 * should cancel tree update instead. 173 */ 174 fatal("out of memory"); 175 } 176 ue->ue_uid = ir.ir_key.ik_uid; 177 len = strlen(ue->ue_line) + 1; 178 ue->ue_line[strcspn(ue->ue_line, ":")] = '\0'; 179 if (RB_INSERT(user_name_tree, env->sc_user_names_t, 180 ue) != NULL) { /* dup */ 181 free(ue->ue_line); 182 free(ue); 183 } else 184 env->sc_user_line_len += len; 185 break; 186 } 187 case IMSG_GRP_ENTRY: { 188 struct groupent *ge; 189 size_t len; 190 191 (void)memcpy(&ir, imsg.data, sizeof(ir)); 192 if ((ge = calloc(1, sizeof(*ge))) == NULL || 193 (ge->ge_line = strdup(ir.ir_line)) == NULL) { 194 /* 195 * should cancel tree update instead. 196 */ 197 fatal("out of memory"); 198 } 199 ge->ge_gid = ir.ir_key.ik_gid; 200 len = strlen(ge->ge_line) + 1; 201 ge->ge_line[strcspn(ge->ge_line, ":")] = '\0'; 202 if (RB_INSERT(group_name_tree, env->sc_group_names_t, 203 ge) != NULL) { /* dup */ 204 free(ge->ge_line); 205 free(ge); 206 } else 207 env->sc_group_line_len += len; 208 break; 209 } 210 case IMSG_TRASH_UPDATE: { 211 struct userent *ue; 212 struct groupent *ge; 213 214 while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) { 215 RB_REMOVE(user_name_tree, 216 env->sc_user_names_t, ue); 217 free(ue->ue_line); 218 free(ue); 219 } 220 free(env->sc_user_names_t); 221 env->sc_user_names_t = NULL; 222 while ((ge = RB_ROOT(env->sc_group_names_t)) 223 != NULL) { 224 RB_REMOVE(group_name_tree, 225 env->sc_group_names_t, ge); 226 free(ge->ge_line); 227 free(ge); 228 } 229 free(env->sc_group_names_t); 230 env->sc_group_names_t = NULL; 231 break; 232 } 233 case IMSG_END_UPDATE: { 234 struct userent *ue; 235 struct groupent *ge; 236 237 log_debug("updates are over, cleaning up trees now"); 238 239 if (env->sc_user_names == NULL) { 240 env->sc_user_names = env->sc_user_names_t; 241 env->sc_user_lines = NULL; 242 env->sc_user_names_t = NULL; 243 244 env->sc_group_names = env->sc_group_names_t; 245 env->sc_group_lines = NULL; 246 env->sc_group_names_t = NULL; 247 248 flatten_entries(env); 249 goto make_uids; 250 } 251 252 /* 253 * clean previous tree. 254 */ 255 while ((ue = RB_ROOT(env->sc_user_names)) != NULL) { 256 RB_REMOVE(user_name_tree, env->sc_user_names, 257 ue); 258 free(ue); 259 } 260 free(env->sc_user_names); 261 free(env->sc_user_lines); 262 263 env->sc_user_names = env->sc_user_names_t; 264 env->sc_user_lines = NULL; 265 env->sc_user_names_t = NULL; 266 267 while ((ge = RB_ROOT(env->sc_group_names)) != NULL) { 268 RB_REMOVE(group_name_tree, 269 env->sc_group_names, ge); 270 free(ge); 271 } 272 free(env->sc_group_names); 273 free(env->sc_group_lines); 274 275 env->sc_group_names = env->sc_group_names_t; 276 env->sc_group_lines = NULL; 277 env->sc_group_names_t = NULL; 278 279 280 flatten_entries(env); 281 282 /* 283 * trees are flat now. build up uid and gid trees. 284 */ 285 286 make_uids: 287 RB_INIT(&env->sc_user_uids); 288 RB_INIT(&env->sc_group_gids); 289 RB_FOREACH(ue, user_name_tree, env->sc_user_names) 290 RB_INSERT(user_uid_tree, 291 &env->sc_user_uids, ue); 292 RB_FOREACH(ge, group_name_tree, env->sc_group_names) 293 RB_INSERT(group_gid_tree, 294 &env->sc_group_gids, ge); 295 break; 296 } 297 default: 298 log_debug("main_dispatch_client: unexpected imsg %d", 299 imsg.hdr.type); 300 break; 301 } 302 imsg_free(&imsg); 303 } 304 if (!shut) 305 imsg_event_add(ibuf); 306 else { 307 log_debug("king bula sez: ran into dead pipe"); 308 event_del(&ibuf->ev); 309 event_loopexit(NULL); 310 } 311 } 312 313 void 314 main_configure_client(struct env *env) 315 { 316 struct idm *idm; 317 struct imsgbuf *ibuf = env->sc_ibuf; 318 319 imsg_compose(ibuf, IMSG_CONF_START, 0, 0, env, sizeof(*env)); 320 TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) { 321 imsg_compose(ibuf, IMSG_CONF_IDM, 0, 0, idm, sizeof(*idm)); 322 } 323 imsg_compose(ibuf, IMSG_CONF_END, 0, 0, NULL, 0); 324 } 325 326 void 327 main_init_timer(int fd, short event, void *p) 328 { 329 struct env *env = p; 330 331 main_configure_client(env); 332 } 333 334 void 335 purge_config(struct env *env) 336 { 337 struct idm *idm; 338 339 while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) { 340 TAILQ_REMOVE(&env->sc_idms, idm, idm_entry); 341 free(idm); 342 } 343 } 344 345 int 346 main(int argc, char *argv[]) 347 { 348 int c; 349 int debug; 350 struct passwd *pw; 351 struct env env; 352 struct event ev_sigint; 353 struct event ev_sigterm; 354 struct event ev_sigchld; 355 struct event ev_sighup; 356 struct event ev_timer; 357 struct timeval tv; 358 359 debug = 0; 360 ypldap_process = PROC_MAIN; 361 362 log_init(1); 363 364 while ((c = getopt(argc, argv, "dD:nf:v")) != -1) { 365 switch (c) { 366 case 'd': 367 debug = 2; 368 break; 369 case 'D': 370 if (cmdline_symset(optarg) < 0) 371 log_warnx("could not parse macro definition %s", 372 optarg); 373 break; 374 case 'n': 375 debug = 2; 376 opts |= YPLDAP_OPT_NOACTION; 377 break; 378 case 'f': 379 conffile = optarg; 380 break; 381 case 'v': 382 opts |= YPLDAP_OPT_VERBOSE; 383 break; 384 default: 385 usage(); 386 } 387 } 388 389 argc -= optind; 390 argv += optind; 391 392 if (argc) 393 usage(); 394 395 RB_INIT(&env.sc_user_uids); 396 RB_INIT(&env.sc_group_gids); 397 398 if (parse_config(&env, conffile, opts)) 399 exit(1); 400 if (opts & YPLDAP_OPT_NOACTION) { 401 fprintf(stderr, "configuration OK\n"); 402 exit(0); 403 } 404 405 if (geteuid()) 406 errx(1, "need root privileges"); 407 408 log_init(debug); 409 410 if (!debug) { 411 if (daemon(1, 0) == -1) 412 err(1, "failed to daemonize"); 413 } 414 415 log_info("startup%s", (debug > 1)?" [debug mode]":""); 416 417 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, 418 pipe_main2client) == -1) 419 fatal("socketpair"); 420 421 set_nonblock(pipe_main2client[0]); 422 set_nonblock(pipe_main2client[1]); 423 424 client_pid = ldapclient(pipe_main2client); 425 426 setproctitle("parent"); 427 event_init(); 428 429 signal_set(&ev_sigint, SIGINT, main_sig_handler, &env); 430 signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env); 431 signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env); 432 signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env); 433 signal_add(&ev_sigint, NULL); 434 signal_add(&ev_sigterm, NULL); 435 signal_add(&ev_sighup, NULL); 436 signal_add(&ev_sigchld, NULL); 437 438 close(pipe_main2client[1]); 439 if ((env.sc_ibuf = calloc(1, sizeof(*env.sc_ibuf))) == NULL) 440 fatal(NULL); 441 imsg_init(env.sc_ibuf, pipe_main2client[0], main_dispatch_client); 442 443 env.sc_ibuf->events = EV_READ; 444 env.sc_ibuf->data = &env; 445 event_set(&env.sc_ibuf->ev, env.sc_ibuf->fd, env.sc_ibuf->events, 446 env.sc_ibuf->handler, &env); 447 event_add(&env.sc_ibuf->ev, NULL); 448 449 yp_init(&env); 450 451 if ((pw = getpwnam(YPLDAP_USER)) == NULL) 452 fatal("getpwnam"); 453 454 #ifndef DEBUG 455 if (setgroups(1, &pw->pw_gid) || 456 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 457 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 458 fatal("cannot drop privileges"); 459 #else 460 #warning disabling privilege revocation in debug mode 461 #endif 462 463 bzero(&tv, sizeof(tv)); 464 evtimer_set(&ev_timer, main_init_timer, &env); 465 evtimer_add(&ev_timer, &tv); 466 467 yp_enable_events(); 468 event_dispatch(); 469 main_shutdown(); 470 471 return (0); 472 } 473 474 void 475 imsg_event_add(struct imsgbuf *ibuf) 476 { 477 struct env *env = ibuf->data; 478 479 ibuf->events = EV_READ; 480 if (ibuf->w.queued) 481 ibuf->events |= EV_WRITE; 482 483 event_del(&ibuf->ev); 484 event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, env); 485 event_add(&ibuf->ev, NULL); 486 } 487 488 void 489 set_nonblock(int fd) 490 { 491 int flags; 492 493 if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 494 fatal("fcntl F_GETFL"); 495 496 flags |= O_NONBLOCK; 497 498 if ((flags = fcntl(fd, F_SETFL, flags)) == -1) 499 fatal("fcntl F_SETFL"); 500 } 501