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