xref: /openbsd-src/usr.sbin/ypldap/ypldap.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
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