ypldap.c revision 290931
1290931Srodrigc/*	$OpenBSD: ypldap.c,v 1.16 2015/11/02 10:06:06 jmatthew Exp $ */
2290931Srodrigc/*	$FreeBSD */
3290931Srodrigc
4290931Srodrigc/*
5290931Srodrigc * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6290931Srodrigc *
7290931Srodrigc * Permission to use, copy, modify, and distribute this software for any
8290931Srodrigc * purpose with or without fee is hereby granted, provided that the above
9290931Srodrigc * copyright notice and this permission notice appear in all copies.
10290931Srodrigc *
11290931Srodrigc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12290931Srodrigc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13290931Srodrigc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14290931Srodrigc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15290931Srodrigc * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16290931Srodrigc * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17290931Srodrigc * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18290931Srodrigc */
19290931Srodrigc
20290931Srodrigc#include <sys/types.h>
21290931Srodrigc#include <sys/queue.h>
22290931Srodrigc#include <sys/socket.h>
23290931Srodrigc#include <sys/signal.h>
24290931Srodrigc#include <sys/tree.h>
25290931Srodrigc#include <sys/wait.h>
26290931Srodrigc
27290931Srodrigc#include <netinet/in.h>
28290931Srodrigc#include <arpa/inet.h>
29290931Srodrigc
30290931Srodrigc#include <err.h>
31290931Srodrigc#include <errno.h>
32290931Srodrigc#include <event.h>
33290931Srodrigc#include <unistd.h>
34290931Srodrigc#include <pwd.h>
35290931Srodrigc#include <stdio.h>
36290931Srodrigc#include <stdlib.h>
37290931Srodrigc#include <string.h>
38290931Srodrigc#include <limits.h>
39290931Srodrigc
40290931Srodrigc#include "ypldap.h"
41290931Srodrigc
42290931Srodrigc__dead void	 usage(void);
43290931Srodrigcint		 check_child(pid_t, const char *);
44290931Srodrigcvoid		 main_sig_handler(int, short, void *);
45290931Srodrigcvoid		 main_shutdown(void);
46290931Srodrigcvoid		 main_dispatch_client(int, short, void *);
47290931Srodrigcvoid		 main_configure_client(struct env *);
48290931Srodrigcvoid		 main_init_timer(int, short, void *);
49290931Srodrigcvoid		 main_start_update(struct env *);
50290931Srodrigcvoid		 main_trash_update(struct env *);
51290931Srodrigcvoid		 main_end_update(struct env *);
52290931Srodrigcint		 main_create_user_groups(struct env *);
53290931Srodrigcvoid		 purge_config(struct env *);
54290931Srodrigcvoid		 reconfigure(struct env *);
55290931Srodrigc
56290931Srodrigcint		 pipe_main2client[2];
57290931Srodrigc
58290931Srodrigcpid_t		 client_pid = 0;
59290931Srodrigcchar		*conffile = YPLDAP_CONF_FILE;
60290931Srodrigcint		 opts = 0;
61290931Srodrigc
62290931Srodrigcvoid
63290931Srodrigcusage(void)
64290931Srodrigc{
65290931Srodrigc	extern const char	*__progname;
66290931Srodrigc
67290931Srodrigc	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
68290931Srodrigc	    __progname);
69290931Srodrigc	exit(1);
70290931Srodrigc}
71290931Srodrigc
72290931Srodrigcint
73290931Srodrigccheck_child(pid_t pid, const char *pname)
74290931Srodrigc{
75290931Srodrigc	int	status;
76290931Srodrigc
77290931Srodrigc	if (waitpid(pid, &status, WNOHANG) > 0) {
78290931Srodrigc		if (WIFEXITED(status)) {
79290931Srodrigc			log_warnx("check_child: lost child %s exited", pname);
80290931Srodrigc			return (1);
81290931Srodrigc		}
82290931Srodrigc		if (WIFSIGNALED(status)) {
83290931Srodrigc			log_warnx("check_child: lost child %s terminated; "
84290931Srodrigc			    "signal %d", pname, WTERMSIG(status));
85290931Srodrigc			return (1);
86290931Srodrigc		}
87290931Srodrigc	}
88290931Srodrigc	return (0);
89290931Srodrigc}
90290931Srodrigc
91290931Srodrigc/* ARGUSED */
92290931Srodrigcvoid
93290931Srodrigcmain_sig_handler(int sig, short event, void *p)
94290931Srodrigc{
95290931Srodrigc	int		 die = 0;
96290931Srodrigc
97290931Srodrigc	switch (sig) {
98290931Srodrigc	case SIGTERM:
99290931Srodrigc	case SIGINT:
100290931Srodrigc		die = 1;
101290931Srodrigc		/* FALLTHROUGH */
102290931Srodrigc	case SIGCHLD:
103290931Srodrigc		if (check_child(client_pid, "ldap client")) {
104290931Srodrigc			client_pid = 0;
105290931Srodrigc			die = 1;
106290931Srodrigc		}
107290931Srodrigc		if (die)
108290931Srodrigc			main_shutdown();
109290931Srodrigc		break;
110290931Srodrigc	case SIGHUP:
111290931Srodrigc		/* reconfigure */
112290931Srodrigc		break;
113290931Srodrigc	default:
114290931Srodrigc		fatalx("unexpected signal");
115290931Srodrigc	}
116290931Srodrigc}
117290931Srodrigc
118290931Srodrigcvoid
119290931Srodrigcmain_shutdown(void)
120290931Srodrigc{
121290931Srodrigc	_exit(0);
122290931Srodrigc}
123290931Srodrigc
124290931Srodrigcvoid
125290931Srodrigcmain_start_update(struct env *env)
126290931Srodrigc{
127290931Srodrigc	env->update_trashed = 0;
128290931Srodrigc
129290931Srodrigc	log_debug("starting directory update");
130290931Srodrigc	env->sc_user_line_len = 0;
131290931Srodrigc	env->sc_group_line_len = 0;
132290931Srodrigc	if ((env->sc_user_names_t = calloc(1,
133290931Srodrigc	    sizeof(*env->sc_user_names_t))) == NULL ||
134290931Srodrigc	    (env->sc_group_names_t = calloc(1,
135290931Srodrigc	    sizeof(*env->sc_group_names_t))) == NULL)
136290931Srodrigc		fatal(NULL);
137290931Srodrigc	RB_INIT(env->sc_user_names_t);
138290931Srodrigc	RB_INIT(env->sc_group_names_t);
139290931Srodrigc}
140290931Srodrigc
141290931Srodrigc/*
142290931Srodrigc * XXX: Currently this function should only be called when updating is
143290931Srodrigc * finished. A notification should be send to ldapclient that it should stop
144290931Srodrigc * sending new pwd/grp entries before it can be called from different places.
145290931Srodrigc */
146290931Srodrigcvoid
147290931Srodrigcmain_trash_update(struct env *env)
148290931Srodrigc{
149290931Srodrigc	struct userent	*ue;
150290931Srodrigc	struct groupent	*ge;
151290931Srodrigc
152290931Srodrigc	env->update_trashed = 1;
153290931Srodrigc
154290931Srodrigc	while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) {
155290931Srodrigc		RB_REMOVE(user_name_tree,
156290931Srodrigc		    env->sc_user_names_t, ue);
157290931Srodrigc		free(ue->ue_line);
158290931Srodrigc		free(ue->ue_netid_line);
159290931Srodrigc		free(ue);
160290931Srodrigc	}
161290931Srodrigc	free(env->sc_user_names_t);
162290931Srodrigc	env->sc_user_names_t = NULL;
163290931Srodrigc	while ((ge = RB_ROOT(env->sc_group_names_t))
164290931Srodrigc	    != NULL) {
165290931Srodrigc		RB_REMOVE(group_name_tree,
166290931Srodrigc		    env->sc_group_names_t, ge);
167290931Srodrigc		free(ge->ge_line);
168290931Srodrigc		free(ge);
169290931Srodrigc	}
170290931Srodrigc	free(env->sc_group_names_t);
171290931Srodrigc	env->sc_group_names_t = NULL;
172290931Srodrigc}
173290931Srodrigc
174290931Srodrigcint
175290931Srodrigcmain_create_user_groups(struct env *env)
176290931Srodrigc{
177290931Srodrigc	struct userent		*ue;
178290931Srodrigc	struct userent		 ukey;
179290931Srodrigc	struct groupent		*ge;
180290931Srodrigc	gid_t			 pw_gid;
181290931Srodrigc	char			*bp, *cp;
182290931Srodrigc	char			*p;
183290931Srodrigc	const char		*errstr = NULL;
184290931Srodrigc	size_t			 len;
185290931Srodrigc
186290931Srodrigc	RB_FOREACH(ue, user_name_tree, env->sc_user_names_t) {
187290931Srodrigc		bp = cp = ue->ue_line;
188290931Srodrigc
189290931Srodrigc		/* name */
190290931Srodrigc		bp += strlen(bp) + 1;
191290931Srodrigc
192290931Srodrigc		/* password */
193290931Srodrigc		bp += strcspn(bp, ":") + 1;
194290931Srodrigc
195290931Srodrigc		/* uid */
196290931Srodrigc		bp += strcspn(bp, ":") + 1;
197290931Srodrigc
198290931Srodrigc		/* gid */
199290931Srodrigc		bp[strcspn(bp, ":")] = '\0';
200290931Srodrigc
201290931Srodrigc		pw_gid = (gid_t)strtonum(bp, 0, GID_MAX, &errstr);
202290931Srodrigc		if (errstr) {
203290931Srodrigc			log_warnx("main: failed to parse gid for uid: %d\n", ue->ue_uid);
204290931Srodrigc			return (-1);
205290931Srodrigc		}
206290931Srodrigc
207290931Srodrigc		/* bring gid column back to its proper state */
208290931Srodrigc		bp[strlen(bp)] = ':';
209290931Srodrigc
210290931Srodrigc		if ((ue->ue_netid_line = calloc(1, LINE_WIDTH)) == NULL) {
211290931Srodrigc			return (-1);
212290931Srodrigc		}
213290931Srodrigc
214290931Srodrigc		if (snprintf(ue->ue_netid_line, LINE_WIDTH-1, "%d:%d", ue->ue_uid, pw_gid) >= LINE_WIDTH) {
215290931Srodrigc
216290931Srodrigc			return (-1);
217290931Srodrigc		}
218290931Srodrigc
219290931Srodrigc		ue->ue_gid = pw_gid;
220290931Srodrigc	}
221290931Srodrigc
222290931Srodrigc	RB_FOREACH(ge, group_name_tree, env->sc_group_names_t) {
223290931Srodrigc		bp = cp = ge->ge_line;
224290931Srodrigc
225290931Srodrigc		/* name */
226290931Srodrigc		bp += strlen(bp) + 1;
227290931Srodrigc
228290931Srodrigc		/* password */
229290931Srodrigc		bp += strcspn(bp, ":") + 1;
230290931Srodrigc
231290931Srodrigc		/* gid */
232290931Srodrigc		bp += strcspn(bp, ":") + 1;
233290931Srodrigc
234290931Srodrigc		cp = bp;
235290931Srodrigc		if (*bp == '\0')
236290931Srodrigc			continue;
237290931Srodrigc		bp = cp;
238290931Srodrigc		for (;;) {
239290931Srodrigc			if (!(cp = strsep(&bp, ",")))
240290931Srodrigc				break;
241290931Srodrigc			ukey.ue_line = cp;
242290931Srodrigc			if ((ue = RB_FIND(user_name_tree, env->sc_user_names_t,
243290931Srodrigc			    &ukey)) == NULL) {
244290931Srodrigc				/* User not found */
245290931Srodrigc				log_warnx("main: user: %s is referenced as a "
246290931Srodrigc					"group member, but can't be found in the "
247290931Srodrigc					"users map.\n", ukey.ue_line);
248290931Srodrigc				if (bp != NULL)
249290931Srodrigc					*(bp-1) = ',';
250290931Srodrigc				continue;
251290931Srodrigc			}
252290931Srodrigc			if (bp != NULL)
253290931Srodrigc				*(bp-1) = ',';
254290931Srodrigc
255290931Srodrigc			/* Make sure the new group doesn't equal to the main gid */
256290931Srodrigc			if (ge->ge_gid == ue->ue_gid)
257290931Srodrigc				continue;
258290931Srodrigc
259290931Srodrigc			len = strlen(ue->ue_netid_line);
260290931Srodrigc			p = ue->ue_netid_line + len;
261290931Srodrigc
262290931Srodrigc			if ((snprintf(p, LINE_WIDTH-len-1, ",%d",
263290931Srodrigc				ge->ge_gid)) >= (int)(LINE_WIDTH-len)) {
264290931Srodrigc				return (-1);
265290931Srodrigc			}
266290931Srodrigc		}
267290931Srodrigc	}
268290931Srodrigc
269290931Srodrigc	return (0);
270290931Srodrigc}
271290931Srodrigc
272290931Srodrigcvoid
273290931Srodrigcmain_end_update(struct env *env)
274290931Srodrigc{
275290931Srodrigc	struct userent		*ue;
276290931Srodrigc	struct groupent		*ge;
277290931Srodrigc
278290931Srodrigc	if (env->update_trashed)
279290931Srodrigc		return;
280290931Srodrigc
281290931Srodrigc	log_debug("updates are over, cleaning up trees now");
282290931Srodrigc
283290931Srodrigc	if (main_create_user_groups(env) == -1) {
284290931Srodrigc		main_trash_update(env);
285290931Srodrigc		return;
286290931Srodrigc	}
287290931Srodrigc
288290931Srodrigc	if (env->sc_user_names == NULL) {
289290931Srodrigc		env->sc_user_names = env->sc_user_names_t;
290290931Srodrigc		env->sc_user_lines = NULL;
291290931Srodrigc		env->sc_user_names_t = NULL;
292290931Srodrigc
293290931Srodrigc		env->sc_group_names = env->sc_group_names_t;
294290931Srodrigc		env->sc_group_lines = NULL;
295290931Srodrigc		env->sc_group_names_t = NULL;
296290931Srodrigc
297290931Srodrigc		flatten_entries(env);
298290931Srodrigc		goto make_uids;
299290931Srodrigc	}
300290931Srodrigc
301290931Srodrigc	/*
302290931Srodrigc	 * clean previous tree.
303290931Srodrigc	 */
304290931Srodrigc	while ((ue = RB_ROOT(env->sc_user_names)) != NULL) {
305290931Srodrigc		RB_REMOVE(user_name_tree, env->sc_user_names,
306290931Srodrigc		    ue);
307290931Srodrigc		free(ue->ue_netid_line);
308290931Srodrigc		free(ue);
309290931Srodrigc	}
310290931Srodrigc	free(env->sc_user_names);
311290931Srodrigc	free(env->sc_user_lines);
312290931Srodrigc
313290931Srodrigc	env->sc_user_names = env->sc_user_names_t;
314290931Srodrigc	env->sc_user_lines = NULL;
315290931Srodrigc	env->sc_user_names_t = NULL;
316290931Srodrigc
317290931Srodrigc	while ((ge = RB_ROOT(env->sc_group_names)) != NULL) {
318290931Srodrigc		RB_REMOVE(group_name_tree,
319290931Srodrigc		    env->sc_group_names, ge);
320290931Srodrigc		free(ge);
321290931Srodrigc	}
322290931Srodrigc	free(env->sc_group_names);
323290931Srodrigc	free(env->sc_group_lines);
324290931Srodrigc
325290931Srodrigc	env->sc_group_names = env->sc_group_names_t;
326290931Srodrigc	env->sc_group_lines = NULL;
327290931Srodrigc	env->sc_group_names_t = NULL;
328290931Srodrigc
329290931Srodrigc
330290931Srodrigc	flatten_entries(env);
331290931Srodrigc
332290931Srodrigc	/*
333290931Srodrigc	 * trees are flat now. build up uid, gid and netid trees.
334290931Srodrigc	 */
335290931Srodrigc
336290931Srodrigcmake_uids:
337290931Srodrigc	RB_INIT(&env->sc_user_uids);
338290931Srodrigc	RB_INIT(&env->sc_group_gids);
339290931Srodrigc	RB_FOREACH(ue, user_name_tree, env->sc_user_names)
340290931Srodrigc		RB_INSERT(user_uid_tree,
341290931Srodrigc		    &env->sc_user_uids, ue);
342290931Srodrigc	RB_FOREACH(ge, group_name_tree, env->sc_group_names)
343290931Srodrigc		RB_INSERT(group_gid_tree,
344290931Srodrigc		    &env->sc_group_gids, ge);
345290931Srodrigc
346290931Srodrigc}
347290931Srodrigc
348290931Srodrigcvoid
349290931Srodrigcmain_dispatch_client(int fd, short events, void *p)
350290931Srodrigc{
351290931Srodrigc	int		 n;
352290931Srodrigc	int		 shut = 0;
353290931Srodrigc	struct env	*env = p;
354290931Srodrigc	struct imsgev	*iev = env->sc_iev;
355290931Srodrigc	struct imsgbuf	*ibuf = &iev->ibuf;
356290931Srodrigc	struct idm_req	 ir;
357290931Srodrigc	struct imsg	 imsg;
358290931Srodrigc
359290931Srodrigc	if ((events & (EV_READ | EV_WRITE)) == 0)
360290931Srodrigc		fatalx("unknown event");
361290931Srodrigc
362290931Srodrigc	if (events & EV_READ) {
363290931Srodrigc		if ((n = imsg_read(ibuf)) == -1)
364290931Srodrigc			fatal("imsg_read error");
365290931Srodrigc		if (n == 0)
366290931Srodrigc			shut = 1;
367290931Srodrigc	}
368290931Srodrigc	if (events & EV_WRITE) {
369290931Srodrigc		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
370290931Srodrigc			fatal("msgbuf_write");
371290931Srodrigc		if (n == 0)
372290931Srodrigc			shut = 1;
373290931Srodrigc		goto done;
374290931Srodrigc	}
375290931Srodrigc
376290931Srodrigc	for (;;) {
377290931Srodrigc		if ((n = imsg_get(ibuf, &imsg)) == -1)
378290931Srodrigc			fatal("main_dispatch_client: imsg_get error");
379290931Srodrigc		if (n == 0)
380290931Srodrigc			break;
381290931Srodrigc
382290931Srodrigc		switch (imsg.hdr.type) {
383290931Srodrigc		case IMSG_START_UPDATE:
384290931Srodrigc			main_start_update(env);
385290931Srodrigc			break;
386290931Srodrigc		case IMSG_PW_ENTRY: {
387290931Srodrigc			struct userent	*ue;
388290931Srodrigc			size_t		 len;
389290931Srodrigc
390290931Srodrigc			if (env->update_trashed)
391290931Srodrigc				break;
392290931Srodrigc
393290931Srodrigc			(void)memcpy(&ir, imsg.data, sizeof(ir));
394290931Srodrigc			if ((ue = calloc(1, sizeof(*ue))) == NULL ||
395290931Srodrigc			    (ue->ue_line = strdup(ir.ir_line)) == NULL) {
396290931Srodrigc				/*
397290931Srodrigc				 * should cancel tree update instead.
398290931Srodrigc				 */
399290931Srodrigc				fatal("out of memory");
400290931Srodrigc			}
401290931Srodrigc			ue->ue_uid = ir.ir_key.ik_uid;
402290931Srodrigc			len = strlen(ue->ue_line) + 1;
403290931Srodrigc			ue->ue_line[strcspn(ue->ue_line, ":")] = '\0';
404290931Srodrigc			if (RB_INSERT(user_name_tree, env->sc_user_names_t,
405290931Srodrigc			    ue) != NULL) { /* dup */
406290931Srodrigc				free(ue->ue_line);
407290931Srodrigc				free(ue);
408290931Srodrigc			} else
409290931Srodrigc				env->sc_user_line_len += len;
410290931Srodrigc			break;
411290931Srodrigc		}
412290931Srodrigc		case IMSG_GRP_ENTRY: {
413290931Srodrigc			struct groupent	*ge;
414290931Srodrigc			size_t		 len;
415290931Srodrigc
416290931Srodrigc			if (env->update_trashed)
417290931Srodrigc				break;
418290931Srodrigc
419290931Srodrigc			(void)memcpy(&ir, imsg.data, sizeof(ir));
420290931Srodrigc			if ((ge = calloc(1, sizeof(*ge))) == NULL ||
421290931Srodrigc			    (ge->ge_line = strdup(ir.ir_line)) == NULL) {
422290931Srodrigc				/*
423290931Srodrigc				 * should cancel tree update instead.
424290931Srodrigc				 */
425290931Srodrigc				fatal("out of memory");
426290931Srodrigc			}
427290931Srodrigc			ge->ge_gid = ir.ir_key.ik_gid;
428290931Srodrigc			len = strlen(ge->ge_line) + 1;
429290931Srodrigc			ge->ge_line[strcspn(ge->ge_line, ":")] = '\0';
430290931Srodrigc			if (RB_INSERT(group_name_tree, env->sc_group_names_t,
431290931Srodrigc			    ge) != NULL) { /* dup */
432290931Srodrigc				free(ge->ge_line);
433290931Srodrigc				free(ge);
434290931Srodrigc			} else
435290931Srodrigc				env->sc_group_line_len += len;
436290931Srodrigc			break;
437290931Srodrigc		}
438290931Srodrigc		case IMSG_TRASH_UPDATE:
439290931Srodrigc			main_trash_update(env);
440290931Srodrigc			break;
441290931Srodrigc		case IMSG_END_UPDATE: {
442290931Srodrigc			main_end_update(env);
443290931Srodrigc			break;
444290931Srodrigc		}
445290931Srodrigc		default:
446290931Srodrigc			log_debug("main_dispatch_client: unexpected imsg %d",
447290931Srodrigc			   imsg.hdr.type);
448290931Srodrigc			break;
449290931Srodrigc		}
450290931Srodrigc		imsg_free(&imsg);
451290931Srodrigc	}
452290931Srodrigc
453290931Srodrigcdone:
454290931Srodrigc	if (!shut)
455290931Srodrigc		imsg_event_add(iev);
456290931Srodrigc	else {
457290931Srodrigc		log_debug("king bula sez: ran into dead pipe");
458290931Srodrigc		event_del(&iev->ev);
459290931Srodrigc		event_loopexit(NULL);
460290931Srodrigc	}
461290931Srodrigc}
462290931Srodrigc
463290931Srodrigcvoid
464290931Srodrigcmain_configure_client(struct env *env)
465290931Srodrigc{
466290931Srodrigc	struct idm	*idm;
467290931Srodrigc	struct imsgev	*iev = env->sc_iev;
468290931Srodrigc
469290931Srodrigc	imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env));
470290931Srodrigc	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
471290931Srodrigc		imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1,
472290931Srodrigc		    idm, sizeof(*idm));
473290931Srodrigc	}
474290931Srodrigc	imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0);
475290931Srodrigc}
476290931Srodrigc
477290931Srodrigcvoid
478290931Srodrigcmain_init_timer(int fd, short event, void *p)
479290931Srodrigc{
480290931Srodrigc	struct env	*env = p;
481290931Srodrigc
482290931Srodrigc	main_configure_client(env);
483290931Srodrigc}
484290931Srodrigc
485290931Srodrigcvoid
486290931Srodrigcpurge_config(struct env *env)
487290931Srodrigc{
488290931Srodrigc	struct idm	*idm;
489290931Srodrigc
490290931Srodrigc	while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) {
491290931Srodrigc		TAILQ_REMOVE(&env->sc_idms, idm, idm_entry);
492290931Srodrigc		free(idm);
493290931Srodrigc	}
494290931Srodrigc}
495290931Srodrigc
496290931Srodrigcint
497290931Srodrigcmain(int argc, char *argv[])
498290931Srodrigc{
499290931Srodrigc	int		 c;
500290931Srodrigc	int		 debug;
501290931Srodrigc	struct passwd	*pw;
502290931Srodrigc	struct env	 env;
503290931Srodrigc	struct event	 ev_sigint;
504290931Srodrigc	struct event	 ev_sigterm;
505290931Srodrigc	struct event	 ev_sigchld;
506290931Srodrigc	struct event	 ev_sighup;
507290931Srodrigc	struct event	 ev_timer;
508290931Srodrigc	struct timeval	 tv;
509290931Srodrigc
510290931Srodrigc	debug = 0;
511290931Srodrigc	ypldap_process = PROC_MAIN;
512290931Srodrigc
513290931Srodrigc	log_init(1);
514290931Srodrigc
515290931Srodrigc	while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
516290931Srodrigc		switch (c) {
517290931Srodrigc		case 'd':
518290931Srodrigc			debug = 2;
519290931Srodrigc			break;
520290931Srodrigc		case 'D':
521290931Srodrigc			if (cmdline_symset(optarg) < 0)
522290931Srodrigc				log_warnx("could not parse macro definition %s",
523290931Srodrigc				    optarg);
524290931Srodrigc			break;
525290931Srodrigc		case 'n':
526290931Srodrigc			debug = 2;
527290931Srodrigc			opts |= YPLDAP_OPT_NOACTION;
528290931Srodrigc			break;
529290931Srodrigc		case 'f':
530290931Srodrigc			conffile = optarg;
531290931Srodrigc			break;
532290931Srodrigc		case 'v':
533290931Srodrigc			opts |= YPLDAP_OPT_VERBOSE;
534290931Srodrigc			break;
535290931Srodrigc		default:
536290931Srodrigc			usage();
537290931Srodrigc		}
538290931Srodrigc	}
539290931Srodrigc
540290931Srodrigc	argc -= optind;
541290931Srodrigc	argv += optind;
542290931Srodrigc
543290931Srodrigc	if (argc)
544290931Srodrigc		usage();
545290931Srodrigc
546290931Srodrigc	RB_INIT(&env.sc_user_uids);
547290931Srodrigc	RB_INIT(&env.sc_group_gids);
548290931Srodrigc
549290931Srodrigc	if (parse_config(&env, conffile, opts))
550290931Srodrigc		exit(1);
551290931Srodrigc	if (opts & YPLDAP_OPT_NOACTION) {
552290931Srodrigc		fprintf(stderr, "configuration OK\n");
553290931Srodrigc		exit(0);
554290931Srodrigc	}
555290931Srodrigc
556290931Srodrigc	if (geteuid())
557290931Srodrigc		errx(1, "need root privileges");
558290931Srodrigc
559290931Srodrigc	log_init(debug);
560290931Srodrigc
561290931Srodrigc	if (!debug) {
562290931Srodrigc		if (daemon(1, 0) == -1)
563290931Srodrigc			err(1, "failed to daemonize");
564290931Srodrigc	}
565290931Srodrigc
566290931Srodrigc	log_info("startup%s", (debug > 1)?" [debug mode]":"");
567290931Srodrigc
568290931Srodrigc	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC,
569290931Srodrigc	    pipe_main2client) == -1)
570290931Srodrigc		fatal("socketpair");
571290931Srodrigc
572290931Srodrigc	client_pid = ldapclient(pipe_main2client);
573290931Srodrigc
574290931Srodrigc	setproctitle("parent");
575290931Srodrigc	event_init();
576290931Srodrigc
577290931Srodrigc	signal_set(&ev_sigint, SIGINT, main_sig_handler, &env);
578290931Srodrigc	signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env);
579290931Srodrigc	signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env);
580290931Srodrigc	signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env);
581290931Srodrigc	signal_add(&ev_sigint, NULL);
582290931Srodrigc	signal_add(&ev_sigterm, NULL);
583290931Srodrigc	signal_add(&ev_sighup, NULL);
584290931Srodrigc	signal_add(&ev_sigchld, NULL);
585290931Srodrigc
586290931Srodrigc	close(pipe_main2client[1]);
587290931Srodrigc	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
588290931Srodrigc		fatal(NULL);
589290931Srodrigc	imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]);
590290931Srodrigc	env.sc_iev->handler = main_dispatch_client;
591290931Srodrigc
592290931Srodrigc	env.sc_iev->events = EV_READ;
593290931Srodrigc	env.sc_iev->data = &env;
594290931Srodrigc	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
595290931Srodrigc	     env.sc_iev->handler, &env);
596290931Srodrigc	event_add(&env.sc_iev->ev, NULL);
597290931Srodrigc
598290931Srodrigc	yp_init(&env);
599290931Srodrigc
600290931Srodrigc	if ((pw = getpwnam(YPLDAP_USER)) == NULL)
601290931Srodrigc		fatal("getpwnam");
602290931Srodrigc
603290931Srodrigc#ifndef DEBUG
604290931Srodrigc	if (setgroups(1, &pw->pw_gid) ||
605290931Srodrigc	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
606290931Srodrigc	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
607290931Srodrigc		fatal("cannot drop privileges");
608290931Srodrigc#else
609290931Srodrigc#warning disabling privilege revocation in debug mode
610290931Srodrigc#endif
611290931Srodrigc
612290931Srodrigc	bzero(&tv, sizeof(tv));
613290931Srodrigc	evtimer_set(&ev_timer, main_init_timer, &env);
614290931Srodrigc	evtimer_add(&ev_timer, &tv);
615290931Srodrigc
616290931Srodrigc	yp_enable_events();
617290931Srodrigc	event_dispatch();
618290931Srodrigc	main_shutdown();
619290931Srodrigc
620290931Srodrigc	return (0);
621290931Srodrigc}
622290931Srodrigc
623290931Srodrigcvoid
624290931Srodrigcimsg_event_add(struct imsgev *iev)
625290931Srodrigc{
626290931Srodrigc	if (iev->handler == NULL) {
627290931Srodrigc		imsg_flush(&iev->ibuf);
628290931Srodrigc		return;
629290931Srodrigc	}
630290931Srodrigc
631290931Srodrigc	iev->events = EV_READ;
632290931Srodrigc	if (iev->ibuf.w.queued)
633290931Srodrigc		iev->events |= EV_WRITE;
634290931Srodrigc
635290931Srodrigc	event_del(&iev->ev);
636290931Srodrigc	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
637290931Srodrigc	event_add(&iev->ev, NULL);
638290931Srodrigc}
639290931Srodrigc
640290931Srodrigcint
641290931Srodrigcimsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
642290931Srodrigc    pid_t pid, int fd, void *data, u_int16_t datalen)
643290931Srodrigc{
644290931Srodrigc	int	ret;
645290931Srodrigc
646290931Srodrigc	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
647290931Srodrigc	    pid, fd, data, datalen)) != -1)
648290931Srodrigc		imsg_event_add(iev);
649290931Srodrigc	return (ret);
650290931Srodrigc}
651