xref: /freebsd/usr.sbin/ypldap/ypldap.c (revision 350ed599829c9ef7e6e51a0636a76d113652ee02)
19e7c127fSCraig Rodrigues /*	$OpenBSD: ypldap.c,v 1.16 2015/11/02 10:06:06 jmatthew Exp $ */
29e7c127fSCraig Rodrigues /*	$FreeBSD */
39e7c127fSCraig Rodrigues 
49e7c127fSCraig Rodrigues /*
59e7c127fSCraig Rodrigues  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
69e7c127fSCraig Rodrigues  *
79e7c127fSCraig Rodrigues  * Permission to use, copy, modify, and distribute this software for any
89e7c127fSCraig Rodrigues  * purpose with or without fee is hereby granted, provided that the above
99e7c127fSCraig Rodrigues  * copyright notice and this permission notice appear in all copies.
109e7c127fSCraig Rodrigues  *
119e7c127fSCraig Rodrigues  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
129e7c127fSCraig Rodrigues  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
139e7c127fSCraig Rodrigues  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
149e7c127fSCraig Rodrigues  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
159e7c127fSCraig Rodrigues  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
169e7c127fSCraig Rodrigues  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
179e7c127fSCraig Rodrigues  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
189e7c127fSCraig Rodrigues  */
199e7c127fSCraig Rodrigues 
209e7c127fSCraig Rodrigues #include <sys/types.h>
2166b5c05dSCraig Rodrigues #include <sys/param.h>
229e7c127fSCraig Rodrigues #include <sys/queue.h>
239e7c127fSCraig Rodrigues #include <sys/socket.h>
249e7c127fSCraig Rodrigues #include <sys/signal.h>
259e7c127fSCraig Rodrigues #include <sys/tree.h>
269e7c127fSCraig Rodrigues #include <sys/wait.h>
279e7c127fSCraig Rodrigues 
289e7c127fSCraig Rodrigues #include <netinet/in.h>
299e7c127fSCraig Rodrigues #include <arpa/inet.h>
309e7c127fSCraig Rodrigues 
319e7c127fSCraig Rodrigues #include <err.h>
329e7c127fSCraig Rodrigues #include <errno.h>
339e7c127fSCraig Rodrigues #include <event.h>
349e7c127fSCraig Rodrigues #include <unistd.h>
359e7c127fSCraig Rodrigues #include <pwd.h>
369e7c127fSCraig Rodrigues #include <stdio.h>
379e7c127fSCraig Rodrigues #include <stdlib.h>
389e7c127fSCraig Rodrigues #include <string.h>
399e7c127fSCraig Rodrigues #include <limits.h>
409e7c127fSCraig Rodrigues 
419e7c127fSCraig Rodrigues #include "ypldap.h"
429e7c127fSCraig Rodrigues 
43*350ed599SCraig Rodrigues __dead2 void	 usage(void);
449e7c127fSCraig Rodrigues int		 check_child(pid_t, const char *);
459e7c127fSCraig Rodrigues void		 main_sig_handler(int, short, void *);
469e7c127fSCraig Rodrigues void		 main_shutdown(void);
479e7c127fSCraig Rodrigues void		 main_dispatch_client(int, short, void *);
489e7c127fSCraig Rodrigues void		 main_configure_client(struct env *);
499e7c127fSCraig Rodrigues void		 main_init_timer(int, short, void *);
509e7c127fSCraig Rodrigues void		 main_start_update(struct env *);
519e7c127fSCraig Rodrigues void		 main_trash_update(struct env *);
529e7c127fSCraig Rodrigues void		 main_end_update(struct env *);
539e7c127fSCraig Rodrigues int		 main_create_user_groups(struct env *);
549e7c127fSCraig Rodrigues void		 purge_config(struct env *);
559e7c127fSCraig Rodrigues void		 reconfigure(struct env *);
569e7c127fSCraig Rodrigues 
579e7c127fSCraig Rodrigues int		 pipe_main2client[2];
589e7c127fSCraig Rodrigues 
599e7c127fSCraig Rodrigues pid_t		 client_pid = 0;
609e7c127fSCraig Rodrigues char		*conffile = YPLDAP_CONF_FILE;
619e7c127fSCraig Rodrigues int		 opts = 0;
629e7c127fSCraig Rodrigues 
639e7c127fSCraig Rodrigues void
649e7c127fSCraig Rodrigues usage(void)
659e7c127fSCraig Rodrigues {
669e7c127fSCraig Rodrigues 	extern const char	*__progname;
679e7c127fSCraig Rodrigues 
689e7c127fSCraig Rodrigues 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
699e7c127fSCraig Rodrigues 	    __progname);
709e7c127fSCraig Rodrigues 	exit(1);
719e7c127fSCraig Rodrigues }
729e7c127fSCraig Rodrigues 
739e7c127fSCraig Rodrigues int
749e7c127fSCraig Rodrigues check_child(pid_t pid, const char *pname)
759e7c127fSCraig Rodrigues {
769e7c127fSCraig Rodrigues 	int	status;
779e7c127fSCraig Rodrigues 
789e7c127fSCraig Rodrigues 	if (waitpid(pid, &status, WNOHANG) > 0) {
799e7c127fSCraig Rodrigues 		if (WIFEXITED(status)) {
809e7c127fSCraig Rodrigues 			log_warnx("check_child: lost child %s exited", pname);
819e7c127fSCraig Rodrigues 			return (1);
829e7c127fSCraig Rodrigues 		}
839e7c127fSCraig Rodrigues 		if (WIFSIGNALED(status)) {
849e7c127fSCraig Rodrigues 			log_warnx("check_child: lost child %s terminated; "
859e7c127fSCraig Rodrigues 			    "signal %d", pname, WTERMSIG(status));
869e7c127fSCraig Rodrigues 			return (1);
879e7c127fSCraig Rodrigues 		}
889e7c127fSCraig Rodrigues 	}
899e7c127fSCraig Rodrigues 	return (0);
909e7c127fSCraig Rodrigues }
919e7c127fSCraig Rodrigues 
929e7c127fSCraig Rodrigues /* ARGUSED */
939e7c127fSCraig Rodrigues void
949e7c127fSCraig Rodrigues main_sig_handler(int sig, short event, void *p)
959e7c127fSCraig Rodrigues {
969e7c127fSCraig Rodrigues 	int		 die = 0;
979e7c127fSCraig Rodrigues 
989e7c127fSCraig Rodrigues 	switch (sig) {
999e7c127fSCraig Rodrigues 	case SIGTERM:
1009e7c127fSCraig Rodrigues 	case SIGINT:
1019e7c127fSCraig Rodrigues 		die = 1;
1029e7c127fSCraig Rodrigues 		/* FALLTHROUGH */
1039e7c127fSCraig Rodrigues 	case SIGCHLD:
1049e7c127fSCraig Rodrigues 		if (check_child(client_pid, "ldap client")) {
1059e7c127fSCraig Rodrigues 			client_pid = 0;
1069e7c127fSCraig Rodrigues 			die = 1;
1079e7c127fSCraig Rodrigues 		}
1089e7c127fSCraig Rodrigues 		if (die)
1099e7c127fSCraig Rodrigues 			main_shutdown();
1109e7c127fSCraig Rodrigues 		break;
1119e7c127fSCraig Rodrigues 	case SIGHUP:
1129e7c127fSCraig Rodrigues 		/* reconfigure */
1139e7c127fSCraig Rodrigues 		break;
1149e7c127fSCraig Rodrigues 	default:
1159e7c127fSCraig Rodrigues 		fatalx("unexpected signal");
1169e7c127fSCraig Rodrigues 	}
1179e7c127fSCraig Rodrigues }
1189e7c127fSCraig Rodrigues 
1199e7c127fSCraig Rodrigues void
1209e7c127fSCraig Rodrigues main_shutdown(void)
1219e7c127fSCraig Rodrigues {
1229e7c127fSCraig Rodrigues 	_exit(0);
1239e7c127fSCraig Rodrigues }
1249e7c127fSCraig Rodrigues 
1259e7c127fSCraig Rodrigues void
1269e7c127fSCraig Rodrigues main_start_update(struct env *env)
1279e7c127fSCraig Rodrigues {
1289e7c127fSCraig Rodrigues 	env->update_trashed = 0;
1299e7c127fSCraig Rodrigues 
1309e7c127fSCraig Rodrigues 	log_debug("starting directory update");
1319e7c127fSCraig Rodrigues 	env->sc_user_line_len = 0;
1329e7c127fSCraig Rodrigues 	env->sc_group_line_len = 0;
1339e7c127fSCraig Rodrigues 	if ((env->sc_user_names_t = calloc(1,
1349e7c127fSCraig Rodrigues 	    sizeof(*env->sc_user_names_t))) == NULL ||
1359e7c127fSCraig Rodrigues 	    (env->sc_group_names_t = calloc(1,
1369e7c127fSCraig Rodrigues 	    sizeof(*env->sc_group_names_t))) == NULL)
1379e7c127fSCraig Rodrigues 		fatal(NULL);
1389e7c127fSCraig Rodrigues 	RB_INIT(env->sc_user_names_t);
1399e7c127fSCraig Rodrigues 	RB_INIT(env->sc_group_names_t);
1409e7c127fSCraig Rodrigues }
1419e7c127fSCraig Rodrigues 
1429e7c127fSCraig Rodrigues /*
1439e7c127fSCraig Rodrigues  * XXX: Currently this function should only be called when updating is
1449e7c127fSCraig Rodrigues  * finished. A notification should be send to ldapclient that it should stop
1459e7c127fSCraig Rodrigues  * sending new pwd/grp entries before it can be called from different places.
1469e7c127fSCraig Rodrigues  */
1479e7c127fSCraig Rodrigues void
1489e7c127fSCraig Rodrigues main_trash_update(struct env *env)
1499e7c127fSCraig Rodrigues {
1509e7c127fSCraig Rodrigues 	struct userent	*ue;
1519e7c127fSCraig Rodrigues 	struct groupent	*ge;
1529e7c127fSCraig Rodrigues 
1539e7c127fSCraig Rodrigues 	env->update_trashed = 1;
1549e7c127fSCraig Rodrigues 
1559e7c127fSCraig Rodrigues 	while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) {
1569e7c127fSCraig Rodrigues 		RB_REMOVE(user_name_tree,
1579e7c127fSCraig Rodrigues 		    env->sc_user_names_t, ue);
1589e7c127fSCraig Rodrigues 		free(ue->ue_line);
1599e7c127fSCraig Rodrigues 		free(ue->ue_netid_line);
1609e7c127fSCraig Rodrigues 		free(ue);
1619e7c127fSCraig Rodrigues 	}
1629e7c127fSCraig Rodrigues 	free(env->sc_user_names_t);
1639e7c127fSCraig Rodrigues 	env->sc_user_names_t = NULL;
1649e7c127fSCraig Rodrigues 	while ((ge = RB_ROOT(env->sc_group_names_t))
1659e7c127fSCraig Rodrigues 	    != NULL) {
1669e7c127fSCraig Rodrigues 		RB_REMOVE(group_name_tree,
1679e7c127fSCraig Rodrigues 		    env->sc_group_names_t, ge);
1689e7c127fSCraig Rodrigues 		free(ge->ge_line);
1699e7c127fSCraig Rodrigues 		free(ge);
1709e7c127fSCraig Rodrigues 	}
1719e7c127fSCraig Rodrigues 	free(env->sc_group_names_t);
1729e7c127fSCraig Rodrigues 	env->sc_group_names_t = NULL;
1739e7c127fSCraig Rodrigues }
1749e7c127fSCraig Rodrigues 
1759e7c127fSCraig Rodrigues int
1769e7c127fSCraig Rodrigues main_create_user_groups(struct env *env)
1779e7c127fSCraig Rodrigues {
1789e7c127fSCraig Rodrigues 	struct userent		*ue;
1799e7c127fSCraig Rodrigues 	struct userent		 ukey;
1809e7c127fSCraig Rodrigues 	struct groupent		*ge;
1819e7c127fSCraig Rodrigues 	gid_t			 pw_gid;
1829e7c127fSCraig Rodrigues 	char			*bp, *cp;
1839e7c127fSCraig Rodrigues 	char			*p;
1849e7c127fSCraig Rodrigues 	const char		*errstr = NULL;
1859e7c127fSCraig Rodrigues 	size_t			 len;
1869e7c127fSCraig Rodrigues 
1879e7c127fSCraig Rodrigues 	RB_FOREACH(ue, user_name_tree, env->sc_user_names_t) {
1889e7c127fSCraig Rodrigues 		bp = cp = ue->ue_line;
1899e7c127fSCraig Rodrigues 
1909e7c127fSCraig Rodrigues 		/* name */
1919e7c127fSCraig Rodrigues 		bp += strlen(bp) + 1;
1929e7c127fSCraig Rodrigues 
1939e7c127fSCraig Rodrigues 		/* password */
1949e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
1959e7c127fSCraig Rodrigues 
1969e7c127fSCraig Rodrigues 		/* uid */
1979e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
1989e7c127fSCraig Rodrigues 
1999e7c127fSCraig Rodrigues 		/* gid */
2009e7c127fSCraig Rodrigues 		bp[strcspn(bp, ":")] = '\0';
2019e7c127fSCraig Rodrigues 
2029e7c127fSCraig Rodrigues 		pw_gid = (gid_t)strtonum(bp, 0, GID_MAX, &errstr);
2039e7c127fSCraig Rodrigues 		if (errstr) {
2049e7c127fSCraig Rodrigues 			log_warnx("main: failed to parse gid for uid: %d\n", ue->ue_uid);
2059e7c127fSCraig Rodrigues 			return (-1);
2069e7c127fSCraig Rodrigues 		}
2079e7c127fSCraig Rodrigues 
2089e7c127fSCraig Rodrigues 		/* bring gid column back to its proper state */
2099e7c127fSCraig Rodrigues 		bp[strlen(bp)] = ':';
2109e7c127fSCraig Rodrigues 
2119e7c127fSCraig Rodrigues 		if ((ue->ue_netid_line = calloc(1, LINE_WIDTH)) == NULL) {
2129e7c127fSCraig Rodrigues 			return (-1);
2139e7c127fSCraig Rodrigues 		}
2149e7c127fSCraig Rodrigues 
2159e7c127fSCraig Rodrigues 		if (snprintf(ue->ue_netid_line, LINE_WIDTH-1, "%d:%d", ue->ue_uid, pw_gid) >= LINE_WIDTH) {
2169e7c127fSCraig Rodrigues 
2179e7c127fSCraig Rodrigues 			return (-1);
2189e7c127fSCraig Rodrigues 		}
2199e7c127fSCraig Rodrigues 
2209e7c127fSCraig Rodrigues 		ue->ue_gid = pw_gid;
2219e7c127fSCraig Rodrigues 	}
2229e7c127fSCraig Rodrigues 
2239e7c127fSCraig Rodrigues 	RB_FOREACH(ge, group_name_tree, env->sc_group_names_t) {
2249e7c127fSCraig Rodrigues 		bp = cp = ge->ge_line;
2259e7c127fSCraig Rodrigues 
2269e7c127fSCraig Rodrigues 		/* name */
2279e7c127fSCraig Rodrigues 		bp += strlen(bp) + 1;
2289e7c127fSCraig Rodrigues 
2299e7c127fSCraig Rodrigues 		/* password */
2309e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
2319e7c127fSCraig Rodrigues 
2329e7c127fSCraig Rodrigues 		/* gid */
2339e7c127fSCraig Rodrigues 		bp += strcspn(bp, ":") + 1;
2349e7c127fSCraig Rodrigues 
2359e7c127fSCraig Rodrigues 		cp = bp;
2369e7c127fSCraig Rodrigues 		if (*bp == '\0')
2379e7c127fSCraig Rodrigues 			continue;
2389e7c127fSCraig Rodrigues 		bp = cp;
2399e7c127fSCraig Rodrigues 		for (;;) {
2409e7c127fSCraig Rodrigues 			if (!(cp = strsep(&bp, ",")))
2419e7c127fSCraig Rodrigues 				break;
2429e7c127fSCraig Rodrigues 			ukey.ue_line = cp;
2439e7c127fSCraig Rodrigues 			if ((ue = RB_FIND(user_name_tree, env->sc_user_names_t,
2449e7c127fSCraig Rodrigues 			    &ukey)) == NULL) {
2459e7c127fSCraig Rodrigues 				/* User not found */
2469e7c127fSCraig Rodrigues 				log_warnx("main: user: %s is referenced as a "
2479e7c127fSCraig Rodrigues 					"group member, but can't be found in the "
2489e7c127fSCraig Rodrigues 					"users map.\n", ukey.ue_line);
2499e7c127fSCraig Rodrigues 				if (bp != NULL)
2509e7c127fSCraig Rodrigues 					*(bp-1) = ',';
2519e7c127fSCraig Rodrigues 				continue;
2529e7c127fSCraig Rodrigues 			}
2539e7c127fSCraig Rodrigues 			if (bp != NULL)
2549e7c127fSCraig Rodrigues 				*(bp-1) = ',';
2559e7c127fSCraig Rodrigues 
2569e7c127fSCraig Rodrigues 			/* Make sure the new group doesn't equal to the main gid */
2579e7c127fSCraig Rodrigues 			if (ge->ge_gid == ue->ue_gid)
2589e7c127fSCraig Rodrigues 				continue;
2599e7c127fSCraig Rodrigues 
2609e7c127fSCraig Rodrigues 			len = strlen(ue->ue_netid_line);
2619e7c127fSCraig Rodrigues 			p = ue->ue_netid_line + len;
2629e7c127fSCraig Rodrigues 
2639e7c127fSCraig Rodrigues 			if ((snprintf(p, LINE_WIDTH-len-1, ",%d",
2649e7c127fSCraig Rodrigues 				ge->ge_gid)) >= (int)(LINE_WIDTH-len)) {
2659e7c127fSCraig Rodrigues 				return (-1);
2669e7c127fSCraig Rodrigues 			}
2679e7c127fSCraig Rodrigues 		}
2689e7c127fSCraig Rodrigues 	}
2699e7c127fSCraig Rodrigues 
2709e7c127fSCraig Rodrigues 	return (0);
2719e7c127fSCraig Rodrigues }
2729e7c127fSCraig Rodrigues 
2739e7c127fSCraig Rodrigues void
2749e7c127fSCraig Rodrigues main_end_update(struct env *env)
2759e7c127fSCraig Rodrigues {
2769e7c127fSCraig Rodrigues 	struct userent		*ue;
2779e7c127fSCraig Rodrigues 	struct groupent		*ge;
2789e7c127fSCraig Rodrigues 
2799e7c127fSCraig Rodrigues 	if (env->update_trashed)
2809e7c127fSCraig Rodrigues 		return;
2819e7c127fSCraig Rodrigues 
2829e7c127fSCraig Rodrigues 	log_debug("updates are over, cleaning up trees now");
2839e7c127fSCraig Rodrigues 
2849e7c127fSCraig Rodrigues 	if (main_create_user_groups(env) == -1) {
2859e7c127fSCraig Rodrigues 		main_trash_update(env);
2869e7c127fSCraig Rodrigues 		return;
2879e7c127fSCraig Rodrigues 	}
2889e7c127fSCraig Rodrigues 
2899e7c127fSCraig Rodrigues 	if (env->sc_user_names == NULL) {
2909e7c127fSCraig Rodrigues 		env->sc_user_names = env->sc_user_names_t;
2919e7c127fSCraig Rodrigues 		env->sc_user_lines = NULL;
2929e7c127fSCraig Rodrigues 		env->sc_user_names_t = NULL;
2939e7c127fSCraig Rodrigues 
2949e7c127fSCraig Rodrigues 		env->sc_group_names = env->sc_group_names_t;
2959e7c127fSCraig Rodrigues 		env->sc_group_lines = NULL;
2969e7c127fSCraig Rodrigues 		env->sc_group_names_t = NULL;
2979e7c127fSCraig Rodrigues 
2989e7c127fSCraig Rodrigues 		flatten_entries(env);
2999e7c127fSCraig Rodrigues 		goto make_uids;
3009e7c127fSCraig Rodrigues 	}
3019e7c127fSCraig Rodrigues 
3029e7c127fSCraig Rodrigues 	/*
3039e7c127fSCraig Rodrigues 	 * clean previous tree.
3049e7c127fSCraig Rodrigues 	 */
3059e7c127fSCraig Rodrigues 	while ((ue = RB_ROOT(env->sc_user_names)) != NULL) {
3069e7c127fSCraig Rodrigues 		RB_REMOVE(user_name_tree, env->sc_user_names,
3079e7c127fSCraig Rodrigues 		    ue);
3089e7c127fSCraig Rodrigues 		free(ue->ue_netid_line);
3099e7c127fSCraig Rodrigues 		free(ue);
3109e7c127fSCraig Rodrigues 	}
3119e7c127fSCraig Rodrigues 	free(env->sc_user_names);
3129e7c127fSCraig Rodrigues 	free(env->sc_user_lines);
3139e7c127fSCraig Rodrigues 
3149e7c127fSCraig Rodrigues 	env->sc_user_names = env->sc_user_names_t;
3159e7c127fSCraig Rodrigues 	env->sc_user_lines = NULL;
3169e7c127fSCraig Rodrigues 	env->sc_user_names_t = NULL;
3179e7c127fSCraig Rodrigues 
3189e7c127fSCraig Rodrigues 	while ((ge = RB_ROOT(env->sc_group_names)) != NULL) {
3199e7c127fSCraig Rodrigues 		RB_REMOVE(group_name_tree,
3209e7c127fSCraig Rodrigues 		    env->sc_group_names, ge);
3219e7c127fSCraig Rodrigues 		free(ge);
3229e7c127fSCraig Rodrigues 	}
3239e7c127fSCraig Rodrigues 	free(env->sc_group_names);
3249e7c127fSCraig Rodrigues 	free(env->sc_group_lines);
3259e7c127fSCraig Rodrigues 
3269e7c127fSCraig Rodrigues 	env->sc_group_names = env->sc_group_names_t;
3279e7c127fSCraig Rodrigues 	env->sc_group_lines = NULL;
3289e7c127fSCraig Rodrigues 	env->sc_group_names_t = NULL;
3299e7c127fSCraig Rodrigues 
3309e7c127fSCraig Rodrigues 
3319e7c127fSCraig Rodrigues 	flatten_entries(env);
3329e7c127fSCraig Rodrigues 
3339e7c127fSCraig Rodrigues 	/*
3349e7c127fSCraig Rodrigues 	 * trees are flat now. build up uid, gid and netid trees.
3359e7c127fSCraig Rodrigues 	 */
3369e7c127fSCraig Rodrigues 
3379e7c127fSCraig Rodrigues make_uids:
3389e7c127fSCraig Rodrigues 	RB_INIT(&env->sc_user_uids);
3399e7c127fSCraig Rodrigues 	RB_INIT(&env->sc_group_gids);
3409e7c127fSCraig Rodrigues 	RB_FOREACH(ue, user_name_tree, env->sc_user_names)
3419e7c127fSCraig Rodrigues 		RB_INSERT(user_uid_tree,
3429e7c127fSCraig Rodrigues 		    &env->sc_user_uids, ue);
3439e7c127fSCraig Rodrigues 	RB_FOREACH(ge, group_name_tree, env->sc_group_names)
3449e7c127fSCraig Rodrigues 		RB_INSERT(group_gid_tree,
3459e7c127fSCraig Rodrigues 		    &env->sc_group_gids, ge);
3469e7c127fSCraig Rodrigues 
3479e7c127fSCraig Rodrigues }
3489e7c127fSCraig Rodrigues 
3499e7c127fSCraig Rodrigues void
3509e7c127fSCraig Rodrigues main_dispatch_client(int fd, short events, void *p)
3519e7c127fSCraig Rodrigues {
3529e7c127fSCraig Rodrigues 	int		 n;
3539e7c127fSCraig Rodrigues 	int		 shut = 0;
3549e7c127fSCraig Rodrigues 	struct env	*env = p;
3559e7c127fSCraig Rodrigues 	struct imsgev	*iev = env->sc_iev;
3569e7c127fSCraig Rodrigues 	struct imsgbuf	*ibuf = &iev->ibuf;
3579e7c127fSCraig Rodrigues 	struct idm_req	 ir;
3589e7c127fSCraig Rodrigues 	struct imsg	 imsg;
3599e7c127fSCraig Rodrigues 
3609e7c127fSCraig Rodrigues 	if ((events & (EV_READ | EV_WRITE)) == 0)
3619e7c127fSCraig Rodrigues 		fatalx("unknown event");
3629e7c127fSCraig Rodrigues 
3639e7c127fSCraig Rodrigues 	if (events & EV_READ) {
3649e7c127fSCraig Rodrigues 		if ((n = imsg_read(ibuf)) == -1)
3659e7c127fSCraig Rodrigues 			fatal("imsg_read error");
3669e7c127fSCraig Rodrigues 		if (n == 0)
3679e7c127fSCraig Rodrigues 			shut = 1;
3689e7c127fSCraig Rodrigues 	}
3699e7c127fSCraig Rodrigues 	if (events & EV_WRITE) {
3709e7c127fSCraig Rodrigues 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
3719e7c127fSCraig Rodrigues 			fatal("msgbuf_write");
3729e7c127fSCraig Rodrigues 		if (n == 0)
3739e7c127fSCraig Rodrigues 			shut = 1;
3749e7c127fSCraig Rodrigues 		goto done;
3759e7c127fSCraig Rodrigues 	}
3769e7c127fSCraig Rodrigues 
3779e7c127fSCraig Rodrigues 	for (;;) {
3789e7c127fSCraig Rodrigues 		if ((n = imsg_get(ibuf, &imsg)) == -1)
3799e7c127fSCraig Rodrigues 			fatal("main_dispatch_client: imsg_get error");
3809e7c127fSCraig Rodrigues 		if (n == 0)
3819e7c127fSCraig Rodrigues 			break;
3829e7c127fSCraig Rodrigues 
3839e7c127fSCraig Rodrigues 		switch (imsg.hdr.type) {
3849e7c127fSCraig Rodrigues 		case IMSG_START_UPDATE:
3859e7c127fSCraig Rodrigues 			main_start_update(env);
3869e7c127fSCraig Rodrigues 			break;
3879e7c127fSCraig Rodrigues 		case IMSG_PW_ENTRY: {
3889e7c127fSCraig Rodrigues 			struct userent	*ue;
3899e7c127fSCraig Rodrigues 			size_t		 len;
3909e7c127fSCraig Rodrigues 
3919e7c127fSCraig Rodrigues 			if (env->update_trashed)
3929e7c127fSCraig Rodrigues 				break;
3939e7c127fSCraig Rodrigues 
3949e7c127fSCraig Rodrigues 			(void)memcpy(&ir, imsg.data, sizeof(ir));
3959e7c127fSCraig Rodrigues 			if ((ue = calloc(1, sizeof(*ue))) == NULL ||
3969e7c127fSCraig Rodrigues 			    (ue->ue_line = strdup(ir.ir_line)) == NULL) {
3979e7c127fSCraig Rodrigues 				/*
3989e7c127fSCraig Rodrigues 				 * should cancel tree update instead.
3999e7c127fSCraig Rodrigues 				 */
4009e7c127fSCraig Rodrigues 				fatal("out of memory");
4019e7c127fSCraig Rodrigues 			}
4029e7c127fSCraig Rodrigues 			ue->ue_uid = ir.ir_key.ik_uid;
4039e7c127fSCraig Rodrigues 			len = strlen(ue->ue_line) + 1;
4049e7c127fSCraig Rodrigues 			ue->ue_line[strcspn(ue->ue_line, ":")] = '\0';
4059e7c127fSCraig Rodrigues 			if (RB_INSERT(user_name_tree, env->sc_user_names_t,
4069e7c127fSCraig Rodrigues 			    ue) != NULL) { /* dup */
4079e7c127fSCraig Rodrigues 				free(ue->ue_line);
4089e7c127fSCraig Rodrigues 				free(ue);
4099e7c127fSCraig Rodrigues 			} else
4109e7c127fSCraig Rodrigues 				env->sc_user_line_len += len;
4119e7c127fSCraig Rodrigues 			break;
4129e7c127fSCraig Rodrigues 		}
4139e7c127fSCraig Rodrigues 		case IMSG_GRP_ENTRY: {
4149e7c127fSCraig Rodrigues 			struct groupent	*ge;
4159e7c127fSCraig Rodrigues 			size_t		 len;
4169e7c127fSCraig Rodrigues 
4179e7c127fSCraig Rodrigues 			if (env->update_trashed)
4189e7c127fSCraig Rodrigues 				break;
4199e7c127fSCraig Rodrigues 
4209e7c127fSCraig Rodrigues 			(void)memcpy(&ir, imsg.data, sizeof(ir));
4219e7c127fSCraig Rodrigues 			if ((ge = calloc(1, sizeof(*ge))) == NULL ||
4229e7c127fSCraig Rodrigues 			    (ge->ge_line = strdup(ir.ir_line)) == NULL) {
4239e7c127fSCraig Rodrigues 				/*
4249e7c127fSCraig Rodrigues 				 * should cancel tree update instead.
4259e7c127fSCraig Rodrigues 				 */
4269e7c127fSCraig Rodrigues 				fatal("out of memory");
4279e7c127fSCraig Rodrigues 			}
4289e7c127fSCraig Rodrigues 			ge->ge_gid = ir.ir_key.ik_gid;
4299e7c127fSCraig Rodrigues 			len = strlen(ge->ge_line) + 1;
4309e7c127fSCraig Rodrigues 			ge->ge_line[strcspn(ge->ge_line, ":")] = '\0';
4319e7c127fSCraig Rodrigues 			if (RB_INSERT(group_name_tree, env->sc_group_names_t,
4329e7c127fSCraig Rodrigues 			    ge) != NULL) { /* dup */
4339e7c127fSCraig Rodrigues 				free(ge->ge_line);
4349e7c127fSCraig Rodrigues 				free(ge);
4359e7c127fSCraig Rodrigues 			} else
4369e7c127fSCraig Rodrigues 				env->sc_group_line_len += len;
4379e7c127fSCraig Rodrigues 			break;
4389e7c127fSCraig Rodrigues 		}
4399e7c127fSCraig Rodrigues 		case IMSG_TRASH_UPDATE:
4409e7c127fSCraig Rodrigues 			main_trash_update(env);
4419e7c127fSCraig Rodrigues 			break;
4429e7c127fSCraig Rodrigues 		case IMSG_END_UPDATE: {
4439e7c127fSCraig Rodrigues 			main_end_update(env);
4449e7c127fSCraig Rodrigues 			break;
4459e7c127fSCraig Rodrigues 		}
4469e7c127fSCraig Rodrigues 		default:
4479e7c127fSCraig Rodrigues 			log_debug("main_dispatch_client: unexpected imsg %d",
4489e7c127fSCraig Rodrigues 			   imsg.hdr.type);
4499e7c127fSCraig Rodrigues 			break;
4509e7c127fSCraig Rodrigues 		}
4519e7c127fSCraig Rodrigues 		imsg_free(&imsg);
4529e7c127fSCraig Rodrigues 	}
4539e7c127fSCraig Rodrigues 
4549e7c127fSCraig Rodrigues done:
4559e7c127fSCraig Rodrigues 	if (!shut)
4569e7c127fSCraig Rodrigues 		imsg_event_add(iev);
4579e7c127fSCraig Rodrigues 	else {
4589e7c127fSCraig Rodrigues 		log_debug("king bula sez: ran into dead pipe");
4599e7c127fSCraig Rodrigues 		event_del(&iev->ev);
4609e7c127fSCraig Rodrigues 		event_loopexit(NULL);
4619e7c127fSCraig Rodrigues 	}
4629e7c127fSCraig Rodrigues }
4639e7c127fSCraig Rodrigues 
4649e7c127fSCraig Rodrigues void
4659e7c127fSCraig Rodrigues main_configure_client(struct env *env)
4669e7c127fSCraig Rodrigues {
4679e7c127fSCraig Rodrigues 	struct idm	*idm;
4689e7c127fSCraig Rodrigues 	struct imsgev	*iev = env->sc_iev;
4699e7c127fSCraig Rodrigues 
4709e7c127fSCraig Rodrigues 	imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env));
4719e7c127fSCraig Rodrigues 	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
4729e7c127fSCraig Rodrigues 		imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1,
4739e7c127fSCraig Rodrigues 		    idm, sizeof(*idm));
4749e7c127fSCraig Rodrigues 	}
4759e7c127fSCraig Rodrigues 	imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0);
4769e7c127fSCraig Rodrigues }
4779e7c127fSCraig Rodrigues 
4789e7c127fSCraig Rodrigues void
4799e7c127fSCraig Rodrigues main_init_timer(int fd, short event, void *p)
4809e7c127fSCraig Rodrigues {
4819e7c127fSCraig Rodrigues 	struct env	*env = p;
4829e7c127fSCraig Rodrigues 
4839e7c127fSCraig Rodrigues 	main_configure_client(env);
4849e7c127fSCraig Rodrigues }
4859e7c127fSCraig Rodrigues 
4869e7c127fSCraig Rodrigues void
4879e7c127fSCraig Rodrigues purge_config(struct env *env)
4889e7c127fSCraig Rodrigues {
4899e7c127fSCraig Rodrigues 	struct idm	*idm;
4909e7c127fSCraig Rodrigues 
4919e7c127fSCraig Rodrigues 	while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) {
4929e7c127fSCraig Rodrigues 		TAILQ_REMOVE(&env->sc_idms, idm, idm_entry);
4939e7c127fSCraig Rodrigues 		free(idm);
4949e7c127fSCraig Rodrigues 	}
4959e7c127fSCraig Rodrigues }
4969e7c127fSCraig Rodrigues 
4979e7c127fSCraig Rodrigues int
4989e7c127fSCraig Rodrigues main(int argc, char *argv[])
4999e7c127fSCraig Rodrigues {
5009e7c127fSCraig Rodrigues 	int		 c;
5019e7c127fSCraig Rodrigues 	int		 debug;
5029e7c127fSCraig Rodrigues 	struct passwd	*pw;
5039e7c127fSCraig Rodrigues 	struct env	 env;
5049e7c127fSCraig Rodrigues 	struct event	 ev_sigint;
5059e7c127fSCraig Rodrigues 	struct event	 ev_sigterm;
5069e7c127fSCraig Rodrigues 	struct event	 ev_sigchld;
5079e7c127fSCraig Rodrigues 	struct event	 ev_sighup;
5089e7c127fSCraig Rodrigues 	struct event	 ev_timer;
5099e7c127fSCraig Rodrigues 	struct timeval	 tv;
5109e7c127fSCraig Rodrigues 
5119e7c127fSCraig Rodrigues 	debug = 0;
5129e7c127fSCraig Rodrigues 	ypldap_process = PROC_MAIN;
5139e7c127fSCraig Rodrigues 
5149e7c127fSCraig Rodrigues 	log_init(1);
5159e7c127fSCraig Rodrigues 
5169e7c127fSCraig Rodrigues 	while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
5179e7c127fSCraig Rodrigues 		switch (c) {
5189e7c127fSCraig Rodrigues 		case 'd':
5199e7c127fSCraig Rodrigues 			debug = 2;
5209e7c127fSCraig Rodrigues 			break;
5219e7c127fSCraig Rodrigues 		case 'D':
5229e7c127fSCraig Rodrigues 			if (cmdline_symset(optarg) < 0)
5239e7c127fSCraig Rodrigues 				log_warnx("could not parse macro definition %s",
5249e7c127fSCraig Rodrigues 				    optarg);
5259e7c127fSCraig Rodrigues 			break;
5269e7c127fSCraig Rodrigues 		case 'n':
5279e7c127fSCraig Rodrigues 			debug = 2;
5289e7c127fSCraig Rodrigues 			opts |= YPLDAP_OPT_NOACTION;
5299e7c127fSCraig Rodrigues 			break;
5309e7c127fSCraig Rodrigues 		case 'f':
5319e7c127fSCraig Rodrigues 			conffile = optarg;
5329e7c127fSCraig Rodrigues 			break;
5339e7c127fSCraig Rodrigues 		case 'v':
5349e7c127fSCraig Rodrigues 			opts |= YPLDAP_OPT_VERBOSE;
5359e7c127fSCraig Rodrigues 			break;
5369e7c127fSCraig Rodrigues 		default:
5379e7c127fSCraig Rodrigues 			usage();
5389e7c127fSCraig Rodrigues 		}
5399e7c127fSCraig Rodrigues 	}
5409e7c127fSCraig Rodrigues 
5419e7c127fSCraig Rodrigues 	argc -= optind;
5429e7c127fSCraig Rodrigues 	argv += optind;
5439e7c127fSCraig Rodrigues 
5449e7c127fSCraig Rodrigues 	if (argc)
5459e7c127fSCraig Rodrigues 		usage();
5469e7c127fSCraig Rodrigues 
5479e7c127fSCraig Rodrigues 	RB_INIT(&env.sc_user_uids);
5489e7c127fSCraig Rodrigues 	RB_INIT(&env.sc_group_gids);
5499e7c127fSCraig Rodrigues 
5509e7c127fSCraig Rodrigues 	if (parse_config(&env, conffile, opts))
5519e7c127fSCraig Rodrigues 		exit(1);
5529e7c127fSCraig Rodrigues 	if (opts & YPLDAP_OPT_NOACTION) {
5539e7c127fSCraig Rodrigues 		fprintf(stderr, "configuration OK\n");
5549e7c127fSCraig Rodrigues 		exit(0);
5559e7c127fSCraig Rodrigues 	}
5569e7c127fSCraig Rodrigues 
5579e7c127fSCraig Rodrigues 	if (geteuid())
5589e7c127fSCraig Rodrigues 		errx(1, "need root privileges");
5599e7c127fSCraig Rodrigues 
5609e7c127fSCraig Rodrigues 	log_init(debug);
5619e7c127fSCraig Rodrigues 
5629e7c127fSCraig Rodrigues 	if (!debug) {
5639e7c127fSCraig Rodrigues 		if (daemon(1, 0) == -1)
5649e7c127fSCraig Rodrigues 			err(1, "failed to daemonize");
5659e7c127fSCraig Rodrigues 	}
5669e7c127fSCraig Rodrigues 
5679e7c127fSCraig Rodrigues 	log_info("startup%s", (debug > 1)?" [debug mode]":"");
5689e7c127fSCraig Rodrigues 
5699e7c127fSCraig Rodrigues 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC,
5709e7c127fSCraig Rodrigues 	    pipe_main2client) == -1)
5719e7c127fSCraig Rodrigues 		fatal("socketpair");
5729e7c127fSCraig Rodrigues 
5739e7c127fSCraig Rodrigues 	client_pid = ldapclient(pipe_main2client);
5749e7c127fSCraig Rodrigues 
5759e7c127fSCraig Rodrigues 	setproctitle("parent");
5769e7c127fSCraig Rodrigues 	event_init();
5779e7c127fSCraig Rodrigues 
5789e7c127fSCraig Rodrigues 	signal_set(&ev_sigint, SIGINT, main_sig_handler, &env);
5799e7c127fSCraig Rodrigues 	signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env);
5809e7c127fSCraig Rodrigues 	signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env);
5819e7c127fSCraig Rodrigues 	signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env);
5829e7c127fSCraig Rodrigues 	signal_add(&ev_sigint, NULL);
5839e7c127fSCraig Rodrigues 	signal_add(&ev_sigterm, NULL);
5849e7c127fSCraig Rodrigues 	signal_add(&ev_sighup, NULL);
5859e7c127fSCraig Rodrigues 	signal_add(&ev_sigchld, NULL);
5869e7c127fSCraig Rodrigues 
5879e7c127fSCraig Rodrigues 	close(pipe_main2client[1]);
5889e7c127fSCraig Rodrigues 	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
5899e7c127fSCraig Rodrigues 		fatal(NULL);
5909e7c127fSCraig Rodrigues 	imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]);
5919e7c127fSCraig Rodrigues 	env.sc_iev->handler = main_dispatch_client;
5929e7c127fSCraig Rodrigues 
5939e7c127fSCraig Rodrigues 	env.sc_iev->events = EV_READ;
5949e7c127fSCraig Rodrigues 	env.sc_iev->data = &env;
5959e7c127fSCraig Rodrigues 	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
5969e7c127fSCraig Rodrigues 	     env.sc_iev->handler, &env);
5979e7c127fSCraig Rodrigues 	event_add(&env.sc_iev->ev, NULL);
5989e7c127fSCraig Rodrigues 
5999e7c127fSCraig Rodrigues 	yp_init(&env);
6009e7c127fSCraig Rodrigues 
6019e7c127fSCraig Rodrigues 	if ((pw = getpwnam(YPLDAP_USER)) == NULL)
6029e7c127fSCraig Rodrigues 		fatal("getpwnam");
6039e7c127fSCraig Rodrigues 
6049e7c127fSCraig Rodrigues #ifndef DEBUG
6059e7c127fSCraig Rodrigues 	if (setgroups(1, &pw->pw_gid) ||
6069e7c127fSCraig Rodrigues 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
6079e7c127fSCraig Rodrigues 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
6089e7c127fSCraig Rodrigues 		fatal("cannot drop privileges");
6099e7c127fSCraig Rodrigues #else
6109e7c127fSCraig Rodrigues #warning disabling privilege revocation in debug mode
6119e7c127fSCraig Rodrigues #endif
6129e7c127fSCraig Rodrigues 
6139e7c127fSCraig Rodrigues 	bzero(&tv, sizeof(tv));
6149e7c127fSCraig Rodrigues 	evtimer_set(&ev_timer, main_init_timer, &env);
6159e7c127fSCraig Rodrigues 	evtimer_add(&ev_timer, &tv);
6169e7c127fSCraig Rodrigues 
6179e7c127fSCraig Rodrigues 	yp_enable_events();
6189e7c127fSCraig Rodrigues 	event_dispatch();
6199e7c127fSCraig Rodrigues 	main_shutdown();
6209e7c127fSCraig Rodrigues 
6219e7c127fSCraig Rodrigues 	return (0);
6229e7c127fSCraig Rodrigues }
6239e7c127fSCraig Rodrigues 
6249e7c127fSCraig Rodrigues void
6259e7c127fSCraig Rodrigues imsg_event_add(struct imsgev *iev)
6269e7c127fSCraig Rodrigues {
6279e7c127fSCraig Rodrigues 	if (iev->handler == NULL) {
6289e7c127fSCraig Rodrigues 		imsg_flush(&iev->ibuf);
6299e7c127fSCraig Rodrigues 		return;
6309e7c127fSCraig Rodrigues 	}
6319e7c127fSCraig Rodrigues 
6329e7c127fSCraig Rodrigues 	iev->events = EV_READ;
6339e7c127fSCraig Rodrigues 	if (iev->ibuf.w.queued)
6349e7c127fSCraig Rodrigues 		iev->events |= EV_WRITE;
6359e7c127fSCraig Rodrigues 
6369e7c127fSCraig Rodrigues 	event_del(&iev->ev);
6379e7c127fSCraig Rodrigues 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
6389e7c127fSCraig Rodrigues 	event_add(&iev->ev, NULL);
6399e7c127fSCraig Rodrigues }
6409e7c127fSCraig Rodrigues 
6419e7c127fSCraig Rodrigues int
6429e7c127fSCraig Rodrigues imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
6439e7c127fSCraig Rodrigues     pid_t pid, int fd, void *data, u_int16_t datalen)
6449e7c127fSCraig Rodrigues {
6459e7c127fSCraig Rodrigues 	int	ret;
6469e7c127fSCraig Rodrigues 
6479e7c127fSCraig Rodrigues 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
6489e7c127fSCraig Rodrigues 	    pid, fd, data, datalen)) != -1)
6499e7c127fSCraig Rodrigues 		imsg_event_add(iev);
6509e7c127fSCraig Rodrigues 	return (ret);
6519e7c127fSCraig Rodrigues }
652