xref: /freebsd/usr.sbin/ypldap/ypldap.c (revision 9e7c127f8d24e1dcf35757ddcad86778ce265092)
1*9e7c127fSCraig Rodrigues /*	$OpenBSD: ypldap.c,v 1.16 2015/11/02 10:06:06 jmatthew Exp $ */
2*9e7c127fSCraig Rodrigues /*	$FreeBSD */
3*9e7c127fSCraig Rodrigues 
4*9e7c127fSCraig Rodrigues /*
5*9e7c127fSCraig Rodrigues  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6*9e7c127fSCraig Rodrigues  *
7*9e7c127fSCraig Rodrigues  * Permission to use, copy, modify, and distribute this software for any
8*9e7c127fSCraig Rodrigues  * purpose with or without fee is hereby granted, provided that the above
9*9e7c127fSCraig Rodrigues  * copyright notice and this permission notice appear in all copies.
10*9e7c127fSCraig Rodrigues  *
11*9e7c127fSCraig Rodrigues  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*9e7c127fSCraig Rodrigues  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*9e7c127fSCraig Rodrigues  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*9e7c127fSCraig Rodrigues  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*9e7c127fSCraig Rodrigues  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*9e7c127fSCraig Rodrigues  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*9e7c127fSCraig Rodrigues  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*9e7c127fSCraig Rodrigues  */
19*9e7c127fSCraig Rodrigues 
20*9e7c127fSCraig Rodrigues #include <sys/types.h>
21*9e7c127fSCraig Rodrigues #include <sys/queue.h>
22*9e7c127fSCraig Rodrigues #include <sys/socket.h>
23*9e7c127fSCraig Rodrigues #include <sys/signal.h>
24*9e7c127fSCraig Rodrigues #include <sys/tree.h>
25*9e7c127fSCraig Rodrigues #include <sys/wait.h>
26*9e7c127fSCraig Rodrigues 
27*9e7c127fSCraig Rodrigues #include <netinet/in.h>
28*9e7c127fSCraig Rodrigues #include <arpa/inet.h>
29*9e7c127fSCraig Rodrigues 
30*9e7c127fSCraig Rodrigues #include <err.h>
31*9e7c127fSCraig Rodrigues #include <errno.h>
32*9e7c127fSCraig Rodrigues #include <event.h>
33*9e7c127fSCraig Rodrigues #include <unistd.h>
34*9e7c127fSCraig Rodrigues #include <pwd.h>
35*9e7c127fSCraig Rodrigues #include <stdio.h>
36*9e7c127fSCraig Rodrigues #include <stdlib.h>
37*9e7c127fSCraig Rodrigues #include <string.h>
38*9e7c127fSCraig Rodrigues #include <limits.h>
39*9e7c127fSCraig Rodrigues 
40*9e7c127fSCraig Rodrigues #include "ypldap.h"
41*9e7c127fSCraig Rodrigues 
42*9e7c127fSCraig Rodrigues __dead void	 usage(void);
43*9e7c127fSCraig Rodrigues int		 check_child(pid_t, const char *);
44*9e7c127fSCraig Rodrigues void		 main_sig_handler(int, short, void *);
45*9e7c127fSCraig Rodrigues void		 main_shutdown(void);
46*9e7c127fSCraig Rodrigues void		 main_dispatch_client(int, short, void *);
47*9e7c127fSCraig Rodrigues void		 main_configure_client(struct env *);
48*9e7c127fSCraig Rodrigues void		 main_init_timer(int, short, void *);
49*9e7c127fSCraig Rodrigues void		 main_start_update(struct env *);
50*9e7c127fSCraig Rodrigues void		 main_trash_update(struct env *);
51*9e7c127fSCraig Rodrigues void		 main_end_update(struct env *);
52*9e7c127fSCraig Rodrigues int		 main_create_user_groups(struct env *);
53*9e7c127fSCraig Rodrigues void		 purge_config(struct env *);
54*9e7c127fSCraig Rodrigues void		 reconfigure(struct env *);
55*9e7c127fSCraig Rodrigues 
56*9e7c127fSCraig Rodrigues int		 pipe_main2client[2];
57*9e7c127fSCraig Rodrigues 
58*9e7c127fSCraig Rodrigues pid_t		 client_pid = 0;
59*9e7c127fSCraig Rodrigues char		*conffile = YPLDAP_CONF_FILE;
60*9e7c127fSCraig Rodrigues int		 opts = 0;
61*9e7c127fSCraig Rodrigues 
62*9e7c127fSCraig Rodrigues void
63*9e7c127fSCraig Rodrigues usage(void)
64*9e7c127fSCraig Rodrigues {
65*9e7c127fSCraig Rodrigues 	extern const char	*__progname;
66*9e7c127fSCraig Rodrigues 
67*9e7c127fSCraig Rodrigues 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
68*9e7c127fSCraig Rodrigues 	    __progname);
69*9e7c127fSCraig Rodrigues 	exit(1);
70*9e7c127fSCraig Rodrigues }
71*9e7c127fSCraig Rodrigues 
72*9e7c127fSCraig Rodrigues int
73*9e7c127fSCraig Rodrigues check_child(pid_t pid, const char *pname)
74*9e7c127fSCraig Rodrigues {
75*9e7c127fSCraig Rodrigues 	int	status;
76*9e7c127fSCraig Rodrigues 
77*9e7c127fSCraig Rodrigues 	if (waitpid(pid, &status, WNOHANG) > 0) {
78*9e7c127fSCraig Rodrigues 		if (WIFEXITED(status)) {
79*9e7c127fSCraig Rodrigues 			log_warnx("check_child: lost child %s exited", pname);
80*9e7c127fSCraig Rodrigues 			return (1);
81*9e7c127fSCraig Rodrigues 		}
82*9e7c127fSCraig Rodrigues 		if (WIFSIGNALED(status)) {
83*9e7c127fSCraig Rodrigues 			log_warnx("check_child: lost child %s terminated; "
84*9e7c127fSCraig Rodrigues 			    "signal %d", pname, WTERMSIG(status));
85*9e7c127fSCraig Rodrigues 			return (1);
86*9e7c127fSCraig Rodrigues 		}
87*9e7c127fSCraig Rodrigues 	}
88*9e7c127fSCraig Rodrigues 	return (0);
89*9e7c127fSCraig Rodrigues }
90*9e7c127fSCraig Rodrigues 
91*9e7c127fSCraig Rodrigues /* ARGUSED */
92*9e7c127fSCraig Rodrigues void
93*9e7c127fSCraig Rodrigues main_sig_handler(int sig, short event, void *p)
94*9e7c127fSCraig Rodrigues {
95*9e7c127fSCraig Rodrigues 	int		 die = 0;
96*9e7c127fSCraig Rodrigues 
97*9e7c127fSCraig Rodrigues 	switch (sig) {
98*9e7c127fSCraig Rodrigues 	case SIGTERM:
99*9e7c127fSCraig Rodrigues 	case SIGINT:
100*9e7c127fSCraig Rodrigues 		die = 1;
101*9e7c127fSCraig Rodrigues 		/* FALLTHROUGH */
102*9e7c127fSCraig Rodrigues 	case SIGCHLD:
103*9e7c127fSCraig Rodrigues 		if (check_child(client_pid, "ldap client")) {
104*9e7c127fSCraig Rodrigues 			client_pid = 0;
105*9e7c127fSCraig Rodrigues 			die = 1;
106*9e7c127fSCraig Rodrigues 		}
107*9e7c127fSCraig Rodrigues 		if (die)
108*9e7c127fSCraig Rodrigues 			main_shutdown();
109*9e7c127fSCraig Rodrigues 		break;
110*9e7c127fSCraig Rodrigues 	case SIGHUP:
111*9e7c127fSCraig Rodrigues 		/* reconfigure */
112*9e7c127fSCraig Rodrigues 		break;
113*9e7c127fSCraig Rodrigues 	default:
114*9e7c127fSCraig Rodrigues 		fatalx("unexpected signal");
115*9e7c127fSCraig Rodrigues 	}
116*9e7c127fSCraig Rodrigues }
117*9e7c127fSCraig Rodrigues 
118*9e7c127fSCraig Rodrigues void
119*9e7c127fSCraig Rodrigues main_shutdown(void)
120*9e7c127fSCraig Rodrigues {
121*9e7c127fSCraig Rodrigues 	_exit(0);
122*9e7c127fSCraig Rodrigues }
123*9e7c127fSCraig Rodrigues 
124*9e7c127fSCraig Rodrigues void
125*9e7c127fSCraig Rodrigues main_start_update(struct env *env)
126*9e7c127fSCraig Rodrigues {
127*9e7c127fSCraig Rodrigues 	env->update_trashed = 0;
128*9e7c127fSCraig Rodrigues 
129*9e7c127fSCraig Rodrigues 	log_debug("starting directory update");
130*9e7c127fSCraig Rodrigues 	env->sc_user_line_len = 0;
131*9e7c127fSCraig Rodrigues 	env->sc_group_line_len = 0;
132*9e7c127fSCraig Rodrigues 	if ((env->sc_user_names_t = calloc(1,
133*9e7c127fSCraig Rodrigues 	    sizeof(*env->sc_user_names_t))) == NULL ||
134*9e7c127fSCraig Rodrigues 	    (env->sc_group_names_t = calloc(1,
135*9e7c127fSCraig Rodrigues 	    sizeof(*env->sc_group_names_t))) == NULL)
136*9e7c127fSCraig Rodrigues 		fatal(NULL);
137*9e7c127fSCraig Rodrigues 	RB_INIT(env->sc_user_names_t);
138*9e7c127fSCraig Rodrigues 	RB_INIT(env->sc_group_names_t);
139*9e7c127fSCraig Rodrigues }
140*9e7c127fSCraig Rodrigues 
141*9e7c127fSCraig Rodrigues /*
142*9e7c127fSCraig Rodrigues  * XXX: Currently this function should only be called when updating is
143*9e7c127fSCraig Rodrigues  * finished. A notification should be send to ldapclient that it should stop
144*9e7c127fSCraig Rodrigues  * sending new pwd/grp entries before it can be called from different places.
145*9e7c127fSCraig Rodrigues  */
146*9e7c127fSCraig Rodrigues void
147*9e7c127fSCraig Rodrigues main_trash_update(struct env *env)
148*9e7c127fSCraig Rodrigues {
149*9e7c127fSCraig Rodrigues 	struct userent	*ue;
150*9e7c127fSCraig Rodrigues 	struct groupent	*ge;
151*9e7c127fSCraig Rodrigues 
152*9e7c127fSCraig Rodrigues 	env->update_trashed = 1;
153*9e7c127fSCraig Rodrigues 
154*9e7c127fSCraig Rodrigues 	while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) {
155*9e7c127fSCraig Rodrigues 		RB_REMOVE(user_name_tree,
156*9e7c127fSCraig Rodrigues 		    env->sc_user_names_t, ue);
157*9e7c127fSCraig Rodrigues 		free(ue->ue_line);
158*9e7c127fSCraig Rodrigues 		free(ue->ue_netid_line);
159*9e7c127fSCraig Rodrigues 		free(ue);
160*9e7c127fSCraig Rodrigues 	}
161*9e7c127fSCraig Rodrigues 	free(env->sc_user_names_t);
162*9e7c127fSCraig Rodrigues 	env->sc_user_names_t = NULL;
163*9e7c127fSCraig Rodrigues 	while ((ge = RB_ROOT(env->sc_group_names_t))
164*9e7c127fSCraig Rodrigues 	    != NULL) {
165*9e7c127fSCraig Rodrigues 		RB_REMOVE(group_name_tree,
166*9e7c127fSCraig Rodrigues 		    env->sc_group_names_t, ge);
167*9e7c127fSCraig Rodrigues 		free(ge->ge_line);
168*9e7c127fSCraig Rodrigues 		free(ge);
169*9e7c127fSCraig Rodrigues 	}
170*9e7c127fSCraig Rodrigues 	free(env->sc_group_names_t);
171*9e7c127fSCraig Rodrigues 	env->sc_group_names_t = NULL;
172*9e7c127fSCraig Rodrigues }
173*9e7c127fSCraig Rodrigues 
174*9e7c127fSCraig Rodrigues int
175*9e7c127fSCraig Rodrigues main_create_user_groups(struct env *env)
176*9e7c127fSCraig Rodrigues {
177*9e7c127fSCraig Rodrigues 	struct userent		*ue;
178*9e7c127fSCraig Rodrigues 	struct userent		 ukey;
179*9e7c127fSCraig Rodrigues 	struct groupent		*ge;
180*9e7c127fSCraig Rodrigues 	gid_t			 pw_gid;
181*9e7c127fSCraig Rodrigues 	char			*bp, *cp;
182*9e7c127fSCraig Rodrigues 	char			*p;
183*9e7c127fSCraig Rodrigues 	const char		*errstr = NULL;
184*9e7c127fSCraig Rodrigues 	size_t			 len;
185*9e7c127fSCraig Rodrigues 
186*9e7c127fSCraig Rodrigues 	RB_FOREACH(ue, user_name_tree, env->sc_user_names_t) {
187*9e7c127fSCraig Rodrigues 		bp = cp = ue->ue_line;
188*9e7c127fSCraig Rodrigues 
189*9e7c127fSCraig Rodrigues 		/* name */
190*9e7c127fSCraig Rodrigues 		bp += strlen(bp) + 1;
191*9e7c127fSCraig Rodrigues 
192*9e7c127fSCraig Rodrigues 		/* password */
193*9e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
194*9e7c127fSCraig Rodrigues 
195*9e7c127fSCraig Rodrigues 		/* uid */
196*9e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
197*9e7c127fSCraig Rodrigues 
198*9e7c127fSCraig Rodrigues 		/* gid */
199*9e7c127fSCraig Rodrigues 		bp[strcspn(bp, ":")] = '\0';
200*9e7c127fSCraig Rodrigues 
201*9e7c127fSCraig Rodrigues 		pw_gid = (gid_t)strtonum(bp, 0, GID_MAX, &errstr);
202*9e7c127fSCraig Rodrigues 		if (errstr) {
203*9e7c127fSCraig Rodrigues 			log_warnx("main: failed to parse gid for uid: %d\n", ue->ue_uid);
204*9e7c127fSCraig Rodrigues 			return (-1);
205*9e7c127fSCraig Rodrigues 		}
206*9e7c127fSCraig Rodrigues 
207*9e7c127fSCraig Rodrigues 		/* bring gid column back to its proper state */
208*9e7c127fSCraig Rodrigues 		bp[strlen(bp)] = ':';
209*9e7c127fSCraig Rodrigues 
210*9e7c127fSCraig Rodrigues 		if ((ue->ue_netid_line = calloc(1, LINE_WIDTH)) == NULL) {
211*9e7c127fSCraig Rodrigues 			return (-1);
212*9e7c127fSCraig Rodrigues 		}
213*9e7c127fSCraig Rodrigues 
214*9e7c127fSCraig Rodrigues 		if (snprintf(ue->ue_netid_line, LINE_WIDTH-1, "%d:%d", ue->ue_uid, pw_gid) >= LINE_WIDTH) {
215*9e7c127fSCraig Rodrigues 
216*9e7c127fSCraig Rodrigues 			return (-1);
217*9e7c127fSCraig Rodrigues 		}
218*9e7c127fSCraig Rodrigues 
219*9e7c127fSCraig Rodrigues 		ue->ue_gid = pw_gid;
220*9e7c127fSCraig Rodrigues 	}
221*9e7c127fSCraig Rodrigues 
222*9e7c127fSCraig Rodrigues 	RB_FOREACH(ge, group_name_tree, env->sc_group_names_t) {
223*9e7c127fSCraig Rodrigues 		bp = cp = ge->ge_line;
224*9e7c127fSCraig Rodrigues 
225*9e7c127fSCraig Rodrigues 		/* name */
226*9e7c127fSCraig Rodrigues 		bp += strlen(bp) + 1;
227*9e7c127fSCraig Rodrigues 
228*9e7c127fSCraig Rodrigues 		/* password */
229*9e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
230*9e7c127fSCraig Rodrigues 
231*9e7c127fSCraig Rodrigues 		/* gid */
232*9e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
233*9e7c127fSCraig Rodrigues 
234*9e7c127fSCraig Rodrigues 		cp = bp;
235*9e7c127fSCraig Rodrigues 		if (*bp == '\0')
236*9e7c127fSCraig Rodrigues 			continue;
237*9e7c127fSCraig Rodrigues 		bp = cp;
238*9e7c127fSCraig Rodrigues 		for (;;) {
239*9e7c127fSCraig Rodrigues 			if (!(cp = strsep(&bp, ",")))
240*9e7c127fSCraig Rodrigues 				break;
241*9e7c127fSCraig Rodrigues 			ukey.ue_line = cp;
242*9e7c127fSCraig Rodrigues 			if ((ue = RB_FIND(user_name_tree, env->sc_user_names_t,
243*9e7c127fSCraig Rodrigues 			    &ukey)) == NULL) {
244*9e7c127fSCraig Rodrigues 				/* User not found */
245*9e7c127fSCraig Rodrigues 				log_warnx("main: user: %s is referenced as a "
246*9e7c127fSCraig Rodrigues 					"group member, but can't be found in the "
247*9e7c127fSCraig Rodrigues 					"users map.\n", ukey.ue_line);
248*9e7c127fSCraig Rodrigues 				if (bp != NULL)
249*9e7c127fSCraig Rodrigues 					*(bp-1) = ',';
250*9e7c127fSCraig Rodrigues 				continue;
251*9e7c127fSCraig Rodrigues 			}
252*9e7c127fSCraig Rodrigues 			if (bp != NULL)
253*9e7c127fSCraig Rodrigues 				*(bp-1) = ',';
254*9e7c127fSCraig Rodrigues 
255*9e7c127fSCraig Rodrigues 			/* Make sure the new group doesn't equal to the main gid */
256*9e7c127fSCraig Rodrigues 			if (ge->ge_gid == ue->ue_gid)
257*9e7c127fSCraig Rodrigues 				continue;
258*9e7c127fSCraig Rodrigues 
259*9e7c127fSCraig Rodrigues 			len = strlen(ue->ue_netid_line);
260*9e7c127fSCraig Rodrigues 			p = ue->ue_netid_line + len;
261*9e7c127fSCraig Rodrigues 
262*9e7c127fSCraig Rodrigues 			if ((snprintf(p, LINE_WIDTH-len-1, ",%d",
263*9e7c127fSCraig Rodrigues 				ge->ge_gid)) >= (int)(LINE_WIDTH-len)) {
264*9e7c127fSCraig Rodrigues 				return (-1);
265*9e7c127fSCraig Rodrigues 			}
266*9e7c127fSCraig Rodrigues 		}
267*9e7c127fSCraig Rodrigues 	}
268*9e7c127fSCraig Rodrigues 
269*9e7c127fSCraig Rodrigues 	return (0);
270*9e7c127fSCraig Rodrigues }
271*9e7c127fSCraig Rodrigues 
272*9e7c127fSCraig Rodrigues void
273*9e7c127fSCraig Rodrigues main_end_update(struct env *env)
274*9e7c127fSCraig Rodrigues {
275*9e7c127fSCraig Rodrigues 	struct userent		*ue;
276*9e7c127fSCraig Rodrigues 	struct groupent		*ge;
277*9e7c127fSCraig Rodrigues 
278*9e7c127fSCraig Rodrigues 	if (env->update_trashed)
279*9e7c127fSCraig Rodrigues 		return;
280*9e7c127fSCraig Rodrigues 
281*9e7c127fSCraig Rodrigues 	log_debug("updates are over, cleaning up trees now");
282*9e7c127fSCraig Rodrigues 
283*9e7c127fSCraig Rodrigues 	if (main_create_user_groups(env) == -1) {
284*9e7c127fSCraig Rodrigues 		main_trash_update(env);
285*9e7c127fSCraig Rodrigues 		return;
286*9e7c127fSCraig Rodrigues 	}
287*9e7c127fSCraig Rodrigues 
288*9e7c127fSCraig Rodrigues 	if (env->sc_user_names == NULL) {
289*9e7c127fSCraig Rodrigues 		env->sc_user_names = env->sc_user_names_t;
290*9e7c127fSCraig Rodrigues 		env->sc_user_lines = NULL;
291*9e7c127fSCraig Rodrigues 		env->sc_user_names_t = NULL;
292*9e7c127fSCraig Rodrigues 
293*9e7c127fSCraig Rodrigues 		env->sc_group_names = env->sc_group_names_t;
294*9e7c127fSCraig Rodrigues 		env->sc_group_lines = NULL;
295*9e7c127fSCraig Rodrigues 		env->sc_group_names_t = NULL;
296*9e7c127fSCraig Rodrigues 
297*9e7c127fSCraig Rodrigues 		flatten_entries(env);
298*9e7c127fSCraig Rodrigues 		goto make_uids;
299*9e7c127fSCraig Rodrigues 	}
300*9e7c127fSCraig Rodrigues 
301*9e7c127fSCraig Rodrigues 	/*
302*9e7c127fSCraig Rodrigues 	 * clean previous tree.
303*9e7c127fSCraig Rodrigues 	 */
304*9e7c127fSCraig Rodrigues 	while ((ue = RB_ROOT(env->sc_user_names)) != NULL) {
305*9e7c127fSCraig Rodrigues 		RB_REMOVE(user_name_tree, env->sc_user_names,
306*9e7c127fSCraig Rodrigues 		    ue);
307*9e7c127fSCraig Rodrigues 		free(ue->ue_netid_line);
308*9e7c127fSCraig Rodrigues 		free(ue);
309*9e7c127fSCraig Rodrigues 	}
310*9e7c127fSCraig Rodrigues 	free(env->sc_user_names);
311*9e7c127fSCraig Rodrigues 	free(env->sc_user_lines);
312*9e7c127fSCraig Rodrigues 
313*9e7c127fSCraig Rodrigues 	env->sc_user_names = env->sc_user_names_t;
314*9e7c127fSCraig Rodrigues 	env->sc_user_lines = NULL;
315*9e7c127fSCraig Rodrigues 	env->sc_user_names_t = NULL;
316*9e7c127fSCraig Rodrigues 
317*9e7c127fSCraig Rodrigues 	while ((ge = RB_ROOT(env->sc_group_names)) != NULL) {
318*9e7c127fSCraig Rodrigues 		RB_REMOVE(group_name_tree,
319*9e7c127fSCraig Rodrigues 		    env->sc_group_names, ge);
320*9e7c127fSCraig Rodrigues 		free(ge);
321*9e7c127fSCraig Rodrigues 	}
322*9e7c127fSCraig Rodrigues 	free(env->sc_group_names);
323*9e7c127fSCraig Rodrigues 	free(env->sc_group_lines);
324*9e7c127fSCraig Rodrigues 
325*9e7c127fSCraig Rodrigues 	env->sc_group_names = env->sc_group_names_t;
326*9e7c127fSCraig Rodrigues 	env->sc_group_lines = NULL;
327*9e7c127fSCraig Rodrigues 	env->sc_group_names_t = NULL;
328*9e7c127fSCraig Rodrigues 
329*9e7c127fSCraig Rodrigues 
330*9e7c127fSCraig Rodrigues 	flatten_entries(env);
331*9e7c127fSCraig Rodrigues 
332*9e7c127fSCraig Rodrigues 	/*
333*9e7c127fSCraig Rodrigues 	 * trees are flat now. build up uid, gid and netid trees.
334*9e7c127fSCraig Rodrigues 	 */
335*9e7c127fSCraig Rodrigues 
336*9e7c127fSCraig Rodrigues make_uids:
337*9e7c127fSCraig Rodrigues 	RB_INIT(&env->sc_user_uids);
338*9e7c127fSCraig Rodrigues 	RB_INIT(&env->sc_group_gids);
339*9e7c127fSCraig Rodrigues 	RB_FOREACH(ue, user_name_tree, env->sc_user_names)
340*9e7c127fSCraig Rodrigues 		RB_INSERT(user_uid_tree,
341*9e7c127fSCraig Rodrigues 		    &env->sc_user_uids, ue);
342*9e7c127fSCraig Rodrigues 	RB_FOREACH(ge, group_name_tree, env->sc_group_names)
343*9e7c127fSCraig Rodrigues 		RB_INSERT(group_gid_tree,
344*9e7c127fSCraig Rodrigues 		    &env->sc_group_gids, ge);
345*9e7c127fSCraig Rodrigues 
346*9e7c127fSCraig Rodrigues }
347*9e7c127fSCraig Rodrigues 
348*9e7c127fSCraig Rodrigues void
349*9e7c127fSCraig Rodrigues main_dispatch_client(int fd, short events, void *p)
350*9e7c127fSCraig Rodrigues {
351*9e7c127fSCraig Rodrigues 	int		 n;
352*9e7c127fSCraig Rodrigues 	int		 shut = 0;
353*9e7c127fSCraig Rodrigues 	struct env	*env = p;
354*9e7c127fSCraig Rodrigues 	struct imsgev	*iev = env->sc_iev;
355*9e7c127fSCraig Rodrigues 	struct imsgbuf	*ibuf = &iev->ibuf;
356*9e7c127fSCraig Rodrigues 	struct idm_req	 ir;
357*9e7c127fSCraig Rodrigues 	struct imsg	 imsg;
358*9e7c127fSCraig Rodrigues 
359*9e7c127fSCraig Rodrigues 	if ((events & (EV_READ | EV_WRITE)) == 0)
360*9e7c127fSCraig Rodrigues 		fatalx("unknown event");
361*9e7c127fSCraig Rodrigues 
362*9e7c127fSCraig Rodrigues 	if (events & EV_READ) {
363*9e7c127fSCraig Rodrigues 		if ((n = imsg_read(ibuf)) == -1)
364*9e7c127fSCraig Rodrigues 			fatal("imsg_read error");
365*9e7c127fSCraig Rodrigues 		if (n == 0)
366*9e7c127fSCraig Rodrigues 			shut = 1;
367*9e7c127fSCraig Rodrigues 	}
368*9e7c127fSCraig Rodrigues 	if (events & EV_WRITE) {
369*9e7c127fSCraig Rodrigues 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
370*9e7c127fSCraig Rodrigues 			fatal("msgbuf_write");
371*9e7c127fSCraig Rodrigues 		if (n == 0)
372*9e7c127fSCraig Rodrigues 			shut = 1;
373*9e7c127fSCraig Rodrigues 		goto done;
374*9e7c127fSCraig Rodrigues 	}
375*9e7c127fSCraig Rodrigues 
376*9e7c127fSCraig Rodrigues 	for (;;) {
377*9e7c127fSCraig Rodrigues 		if ((n = imsg_get(ibuf, &imsg)) == -1)
378*9e7c127fSCraig Rodrigues 			fatal("main_dispatch_client: imsg_get error");
379*9e7c127fSCraig Rodrigues 		if (n == 0)
380*9e7c127fSCraig Rodrigues 			break;
381*9e7c127fSCraig Rodrigues 
382*9e7c127fSCraig Rodrigues 		switch (imsg.hdr.type) {
383*9e7c127fSCraig Rodrigues 		case IMSG_START_UPDATE:
384*9e7c127fSCraig Rodrigues 			main_start_update(env);
385*9e7c127fSCraig Rodrigues 			break;
386*9e7c127fSCraig Rodrigues 		case IMSG_PW_ENTRY: {
387*9e7c127fSCraig Rodrigues 			struct userent	*ue;
388*9e7c127fSCraig Rodrigues 			size_t		 len;
389*9e7c127fSCraig Rodrigues 
390*9e7c127fSCraig Rodrigues 			if (env->update_trashed)
391*9e7c127fSCraig Rodrigues 				break;
392*9e7c127fSCraig Rodrigues 
393*9e7c127fSCraig Rodrigues 			(void)memcpy(&ir, imsg.data, sizeof(ir));
394*9e7c127fSCraig Rodrigues 			if ((ue = calloc(1, sizeof(*ue))) == NULL ||
395*9e7c127fSCraig Rodrigues 			    (ue->ue_line = strdup(ir.ir_line)) == NULL) {
396*9e7c127fSCraig Rodrigues 				/*
397*9e7c127fSCraig Rodrigues 				 * should cancel tree update instead.
398*9e7c127fSCraig Rodrigues 				 */
399*9e7c127fSCraig Rodrigues 				fatal("out of memory");
400*9e7c127fSCraig Rodrigues 			}
401*9e7c127fSCraig Rodrigues 			ue->ue_uid = ir.ir_key.ik_uid;
402*9e7c127fSCraig Rodrigues 			len = strlen(ue->ue_line) + 1;
403*9e7c127fSCraig Rodrigues 			ue->ue_line[strcspn(ue->ue_line, ":")] = '\0';
404*9e7c127fSCraig Rodrigues 			if (RB_INSERT(user_name_tree, env->sc_user_names_t,
405*9e7c127fSCraig Rodrigues 			    ue) != NULL) { /* dup */
406*9e7c127fSCraig Rodrigues 				free(ue->ue_line);
407*9e7c127fSCraig Rodrigues 				free(ue);
408*9e7c127fSCraig Rodrigues 			} else
409*9e7c127fSCraig Rodrigues 				env->sc_user_line_len += len;
410*9e7c127fSCraig Rodrigues 			break;
411*9e7c127fSCraig Rodrigues 		}
412*9e7c127fSCraig Rodrigues 		case IMSG_GRP_ENTRY: {
413*9e7c127fSCraig Rodrigues 			struct groupent	*ge;
414*9e7c127fSCraig Rodrigues 			size_t		 len;
415*9e7c127fSCraig Rodrigues 
416*9e7c127fSCraig Rodrigues 			if (env->update_trashed)
417*9e7c127fSCraig Rodrigues 				break;
418*9e7c127fSCraig Rodrigues 
419*9e7c127fSCraig Rodrigues 			(void)memcpy(&ir, imsg.data, sizeof(ir));
420*9e7c127fSCraig Rodrigues 			if ((ge = calloc(1, sizeof(*ge))) == NULL ||
421*9e7c127fSCraig Rodrigues 			    (ge->ge_line = strdup(ir.ir_line)) == NULL) {
422*9e7c127fSCraig Rodrigues 				/*
423*9e7c127fSCraig Rodrigues 				 * should cancel tree update instead.
424*9e7c127fSCraig Rodrigues 				 */
425*9e7c127fSCraig Rodrigues 				fatal("out of memory");
426*9e7c127fSCraig Rodrigues 			}
427*9e7c127fSCraig Rodrigues 			ge->ge_gid = ir.ir_key.ik_gid;
428*9e7c127fSCraig Rodrigues 			len = strlen(ge->ge_line) + 1;
429*9e7c127fSCraig Rodrigues 			ge->ge_line[strcspn(ge->ge_line, ":")] = '\0';
430*9e7c127fSCraig Rodrigues 			if (RB_INSERT(group_name_tree, env->sc_group_names_t,
431*9e7c127fSCraig Rodrigues 			    ge) != NULL) { /* dup */
432*9e7c127fSCraig Rodrigues 				free(ge->ge_line);
433*9e7c127fSCraig Rodrigues 				free(ge);
434*9e7c127fSCraig Rodrigues 			} else
435*9e7c127fSCraig Rodrigues 				env->sc_group_line_len += len;
436*9e7c127fSCraig Rodrigues 			break;
437*9e7c127fSCraig Rodrigues 		}
438*9e7c127fSCraig Rodrigues 		case IMSG_TRASH_UPDATE:
439*9e7c127fSCraig Rodrigues 			main_trash_update(env);
440*9e7c127fSCraig Rodrigues 			break;
441*9e7c127fSCraig Rodrigues 		case IMSG_END_UPDATE: {
442*9e7c127fSCraig Rodrigues 			main_end_update(env);
443*9e7c127fSCraig Rodrigues 			break;
444*9e7c127fSCraig Rodrigues 		}
445*9e7c127fSCraig Rodrigues 		default:
446*9e7c127fSCraig Rodrigues 			log_debug("main_dispatch_client: unexpected imsg %d",
447*9e7c127fSCraig Rodrigues 			   imsg.hdr.type);
448*9e7c127fSCraig Rodrigues 			break;
449*9e7c127fSCraig Rodrigues 		}
450*9e7c127fSCraig Rodrigues 		imsg_free(&imsg);
451*9e7c127fSCraig Rodrigues 	}
452*9e7c127fSCraig Rodrigues 
453*9e7c127fSCraig Rodrigues done:
454*9e7c127fSCraig Rodrigues 	if (!shut)
455*9e7c127fSCraig Rodrigues 		imsg_event_add(iev);
456*9e7c127fSCraig Rodrigues 	else {
457*9e7c127fSCraig Rodrigues 		log_debug("king bula sez: ran into dead pipe");
458*9e7c127fSCraig Rodrigues 		event_del(&iev->ev);
459*9e7c127fSCraig Rodrigues 		event_loopexit(NULL);
460*9e7c127fSCraig Rodrigues 	}
461*9e7c127fSCraig Rodrigues }
462*9e7c127fSCraig Rodrigues 
463*9e7c127fSCraig Rodrigues void
464*9e7c127fSCraig Rodrigues main_configure_client(struct env *env)
465*9e7c127fSCraig Rodrigues {
466*9e7c127fSCraig Rodrigues 	struct idm	*idm;
467*9e7c127fSCraig Rodrigues 	struct imsgev	*iev = env->sc_iev;
468*9e7c127fSCraig Rodrigues 
469*9e7c127fSCraig Rodrigues 	imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env));
470*9e7c127fSCraig Rodrigues 	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
471*9e7c127fSCraig Rodrigues 		imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1,
472*9e7c127fSCraig Rodrigues 		    idm, sizeof(*idm));
473*9e7c127fSCraig Rodrigues 	}
474*9e7c127fSCraig Rodrigues 	imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0);
475*9e7c127fSCraig Rodrigues }
476*9e7c127fSCraig Rodrigues 
477*9e7c127fSCraig Rodrigues void
478*9e7c127fSCraig Rodrigues main_init_timer(int fd, short event, void *p)
479*9e7c127fSCraig Rodrigues {
480*9e7c127fSCraig Rodrigues 	struct env	*env = p;
481*9e7c127fSCraig Rodrigues 
482*9e7c127fSCraig Rodrigues 	main_configure_client(env);
483*9e7c127fSCraig Rodrigues }
484*9e7c127fSCraig Rodrigues 
485*9e7c127fSCraig Rodrigues void
486*9e7c127fSCraig Rodrigues purge_config(struct env *env)
487*9e7c127fSCraig Rodrigues {
488*9e7c127fSCraig Rodrigues 	struct idm	*idm;
489*9e7c127fSCraig Rodrigues 
490*9e7c127fSCraig Rodrigues 	while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) {
491*9e7c127fSCraig Rodrigues 		TAILQ_REMOVE(&env->sc_idms, idm, idm_entry);
492*9e7c127fSCraig Rodrigues 		free(idm);
493*9e7c127fSCraig Rodrigues 	}
494*9e7c127fSCraig Rodrigues }
495*9e7c127fSCraig Rodrigues 
496*9e7c127fSCraig Rodrigues int
497*9e7c127fSCraig Rodrigues main(int argc, char *argv[])
498*9e7c127fSCraig Rodrigues {
499*9e7c127fSCraig Rodrigues 	int		 c;
500*9e7c127fSCraig Rodrigues 	int		 debug;
501*9e7c127fSCraig Rodrigues 	struct passwd	*pw;
502*9e7c127fSCraig Rodrigues 	struct env	 env;
503*9e7c127fSCraig Rodrigues 	struct event	 ev_sigint;
504*9e7c127fSCraig Rodrigues 	struct event	 ev_sigterm;
505*9e7c127fSCraig Rodrigues 	struct event	 ev_sigchld;
506*9e7c127fSCraig Rodrigues 	struct event	 ev_sighup;
507*9e7c127fSCraig Rodrigues 	struct event	 ev_timer;
508*9e7c127fSCraig Rodrigues 	struct timeval	 tv;
509*9e7c127fSCraig Rodrigues 
510*9e7c127fSCraig Rodrigues 	debug = 0;
511*9e7c127fSCraig Rodrigues 	ypldap_process = PROC_MAIN;
512*9e7c127fSCraig Rodrigues 
513*9e7c127fSCraig Rodrigues 	log_init(1);
514*9e7c127fSCraig Rodrigues 
515*9e7c127fSCraig Rodrigues 	while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
516*9e7c127fSCraig Rodrigues 		switch (c) {
517*9e7c127fSCraig Rodrigues 		case 'd':
518*9e7c127fSCraig Rodrigues 			debug = 2;
519*9e7c127fSCraig Rodrigues 			break;
520*9e7c127fSCraig Rodrigues 		case 'D':
521*9e7c127fSCraig Rodrigues 			if (cmdline_symset(optarg) < 0)
522*9e7c127fSCraig Rodrigues 				log_warnx("could not parse macro definition %s",
523*9e7c127fSCraig Rodrigues 				    optarg);
524*9e7c127fSCraig Rodrigues 			break;
525*9e7c127fSCraig Rodrigues 		case 'n':
526*9e7c127fSCraig Rodrigues 			debug = 2;
527*9e7c127fSCraig Rodrigues 			opts |= YPLDAP_OPT_NOACTION;
528*9e7c127fSCraig Rodrigues 			break;
529*9e7c127fSCraig Rodrigues 		case 'f':
530*9e7c127fSCraig Rodrigues 			conffile = optarg;
531*9e7c127fSCraig Rodrigues 			break;
532*9e7c127fSCraig Rodrigues 		case 'v':
533*9e7c127fSCraig Rodrigues 			opts |= YPLDAP_OPT_VERBOSE;
534*9e7c127fSCraig Rodrigues 			break;
535*9e7c127fSCraig Rodrigues 		default:
536*9e7c127fSCraig Rodrigues 			usage();
537*9e7c127fSCraig Rodrigues 		}
538*9e7c127fSCraig Rodrigues 	}
539*9e7c127fSCraig Rodrigues 
540*9e7c127fSCraig Rodrigues 	argc -= optind;
541*9e7c127fSCraig Rodrigues 	argv += optind;
542*9e7c127fSCraig Rodrigues 
543*9e7c127fSCraig Rodrigues 	if (argc)
544*9e7c127fSCraig Rodrigues 		usage();
545*9e7c127fSCraig Rodrigues 
546*9e7c127fSCraig Rodrigues 	RB_INIT(&env.sc_user_uids);
547*9e7c127fSCraig Rodrigues 	RB_INIT(&env.sc_group_gids);
548*9e7c127fSCraig Rodrigues 
549*9e7c127fSCraig Rodrigues 	if (parse_config(&env, conffile, opts))
550*9e7c127fSCraig Rodrigues 		exit(1);
551*9e7c127fSCraig Rodrigues 	if (opts & YPLDAP_OPT_NOACTION) {
552*9e7c127fSCraig Rodrigues 		fprintf(stderr, "configuration OK\n");
553*9e7c127fSCraig Rodrigues 		exit(0);
554*9e7c127fSCraig Rodrigues 	}
555*9e7c127fSCraig Rodrigues 
556*9e7c127fSCraig Rodrigues 	if (geteuid())
557*9e7c127fSCraig Rodrigues 		errx(1, "need root privileges");
558*9e7c127fSCraig Rodrigues 
559*9e7c127fSCraig Rodrigues 	log_init(debug);
560*9e7c127fSCraig Rodrigues 
561*9e7c127fSCraig Rodrigues 	if (!debug) {
562*9e7c127fSCraig Rodrigues 		if (daemon(1, 0) == -1)
563*9e7c127fSCraig Rodrigues 			err(1, "failed to daemonize");
564*9e7c127fSCraig Rodrigues 	}
565*9e7c127fSCraig Rodrigues 
566*9e7c127fSCraig Rodrigues 	log_info("startup%s", (debug > 1)?" [debug mode]":"");
567*9e7c127fSCraig Rodrigues 
568*9e7c127fSCraig Rodrigues 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC,
569*9e7c127fSCraig Rodrigues 	    pipe_main2client) == -1)
570*9e7c127fSCraig Rodrigues 		fatal("socketpair");
571*9e7c127fSCraig Rodrigues 
572*9e7c127fSCraig Rodrigues 	client_pid = ldapclient(pipe_main2client);
573*9e7c127fSCraig Rodrigues 
574*9e7c127fSCraig Rodrigues 	setproctitle("parent");
575*9e7c127fSCraig Rodrigues 	event_init();
576*9e7c127fSCraig Rodrigues 
577*9e7c127fSCraig Rodrigues 	signal_set(&ev_sigint, SIGINT, main_sig_handler, &env);
578*9e7c127fSCraig Rodrigues 	signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env);
579*9e7c127fSCraig Rodrigues 	signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env);
580*9e7c127fSCraig Rodrigues 	signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env);
581*9e7c127fSCraig Rodrigues 	signal_add(&ev_sigint, NULL);
582*9e7c127fSCraig Rodrigues 	signal_add(&ev_sigterm, NULL);
583*9e7c127fSCraig Rodrigues 	signal_add(&ev_sighup, NULL);
584*9e7c127fSCraig Rodrigues 	signal_add(&ev_sigchld, NULL);
585*9e7c127fSCraig Rodrigues 
586*9e7c127fSCraig Rodrigues 	close(pipe_main2client[1]);
587*9e7c127fSCraig Rodrigues 	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
588*9e7c127fSCraig Rodrigues 		fatal(NULL);
589*9e7c127fSCraig Rodrigues 	imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]);
590*9e7c127fSCraig Rodrigues 	env.sc_iev->handler = main_dispatch_client;
591*9e7c127fSCraig Rodrigues 
592*9e7c127fSCraig Rodrigues 	env.sc_iev->events = EV_READ;
593*9e7c127fSCraig Rodrigues 	env.sc_iev->data = &env;
594*9e7c127fSCraig Rodrigues 	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
595*9e7c127fSCraig Rodrigues 	     env.sc_iev->handler, &env);
596*9e7c127fSCraig Rodrigues 	event_add(&env.sc_iev->ev, NULL);
597*9e7c127fSCraig Rodrigues 
598*9e7c127fSCraig Rodrigues 	yp_init(&env);
599*9e7c127fSCraig Rodrigues 
600*9e7c127fSCraig Rodrigues 	if ((pw = getpwnam(YPLDAP_USER)) == NULL)
601*9e7c127fSCraig Rodrigues 		fatal("getpwnam");
602*9e7c127fSCraig Rodrigues 
603*9e7c127fSCraig Rodrigues #ifndef DEBUG
604*9e7c127fSCraig Rodrigues 	if (setgroups(1, &pw->pw_gid) ||
605*9e7c127fSCraig Rodrigues 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
606*9e7c127fSCraig Rodrigues 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
607*9e7c127fSCraig Rodrigues 		fatal("cannot drop privileges");
608*9e7c127fSCraig Rodrigues #else
609*9e7c127fSCraig Rodrigues #warning disabling privilege revocation in debug mode
610*9e7c127fSCraig Rodrigues #endif
611*9e7c127fSCraig Rodrigues 
612*9e7c127fSCraig Rodrigues 	bzero(&tv, sizeof(tv));
613*9e7c127fSCraig Rodrigues 	evtimer_set(&ev_timer, main_init_timer, &env);
614*9e7c127fSCraig Rodrigues 	evtimer_add(&ev_timer, &tv);
615*9e7c127fSCraig Rodrigues 
616*9e7c127fSCraig Rodrigues 	yp_enable_events();
617*9e7c127fSCraig Rodrigues 	event_dispatch();
618*9e7c127fSCraig Rodrigues 	main_shutdown();
619*9e7c127fSCraig Rodrigues 
620*9e7c127fSCraig Rodrigues 	return (0);
621*9e7c127fSCraig Rodrigues }
622*9e7c127fSCraig Rodrigues 
623*9e7c127fSCraig Rodrigues void
624*9e7c127fSCraig Rodrigues imsg_event_add(struct imsgev *iev)
625*9e7c127fSCraig Rodrigues {
626*9e7c127fSCraig Rodrigues 	if (iev->handler == NULL) {
627*9e7c127fSCraig Rodrigues 		imsg_flush(&iev->ibuf);
628*9e7c127fSCraig Rodrigues 		return;
629*9e7c127fSCraig Rodrigues 	}
630*9e7c127fSCraig Rodrigues 
631*9e7c127fSCraig Rodrigues 	iev->events = EV_READ;
632*9e7c127fSCraig Rodrigues 	if (iev->ibuf.w.queued)
633*9e7c127fSCraig Rodrigues 		iev->events |= EV_WRITE;
634*9e7c127fSCraig Rodrigues 
635*9e7c127fSCraig Rodrigues 	event_del(&iev->ev);
636*9e7c127fSCraig Rodrigues 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
637*9e7c127fSCraig Rodrigues 	event_add(&iev->ev, NULL);
638*9e7c127fSCraig Rodrigues }
639*9e7c127fSCraig Rodrigues 
640*9e7c127fSCraig Rodrigues int
641*9e7c127fSCraig Rodrigues imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
642*9e7c127fSCraig Rodrigues     pid_t pid, int fd, void *data, u_int16_t datalen)
643*9e7c127fSCraig Rodrigues {
644*9e7c127fSCraig Rodrigues 	int	ret;
645*9e7c127fSCraig Rodrigues 
646*9e7c127fSCraig Rodrigues 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
647*9e7c127fSCraig Rodrigues 	    pid, fd, data, datalen)) != -1)
648*9e7c127fSCraig Rodrigues 		imsg_event_add(iev);
649*9e7c127fSCraig Rodrigues 	return (ret);
650*9e7c127fSCraig Rodrigues }
651