ldapclient.c revision 7e0cb4103fed91c6df84012e9062074dfe394c45
1d2078e3rodrigc/* $OpenBSD: ldapclient.c,v 1.31 2014/11/16 23:24:44 tedu Exp $ */
2d2078e3rodrigc/* $FreeBSD$ */
3d2078e3rodrigc
4d2078e3rodrigc/*
5d2078e3rodrigc * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
6d2078e3rodrigc * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
7d2078e3rodrigc *
8d2078e3rodrigc * Permission to use, copy, modify, and distribute this software for any
9d2078e3rodrigc * purpose with or without fee is hereby granted, provided that the above
10d2078e3rodrigc * copyright notice and this permission notice appear in all copies.
11d2078e3rodrigc *
12d2078e3rodrigc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13d2078e3rodrigc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14d2078e3rodrigc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15d2078e3rodrigc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16d2078e3rodrigc * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17d2078e3rodrigc * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18d2078e3rodrigc * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19d2078e3rodrigc */
20d2078e3rodrigc
21d2078e3rodrigc#include <sys/types.h>
22d2078e3rodrigc#include <sys/param.h>
23d2078e3rodrigc#include <sys/queue.h>
24d2078e3rodrigc#include <sys/socket.h>
25d2078e3rodrigc#include <sys/tree.h>
26d2078e3rodrigc
27d2078e3rodrigc#include <netinet/in.h>
28d2078e3rodrigc#include <arpa/inet.h>
29d2078e3rodrigc
30d2078e3rodrigc#include <netdb.h>
31d2078e3rodrigc#include <errno.h>
32d2078e3rodrigc#include <err.h>
33d2078e3rodrigc#include <event.h>
34d2078e3rodrigc#include <fcntl.h>
35d2078e3rodrigc#include <unistd.h>
36d2078e3rodrigc#include <pwd.h>
374fa7625rodrigc#include <signal.h>
38d2078e3rodrigc#include <stdio.h>
39d2078e3rodrigc#include <stdlib.h>
40d2078e3rodrigc#include <string.h>
41d2078e3rodrigc
42d2078e3rodrigc#include "aldap.h"
43d2078e3rodrigc#include "ypldap.h"
44d2078e3rodrigc
45d2078e3rodrigcvoid    client_sig_handler(int, short, void *);
46d2078e3rodrigcvoid	client_dispatch_dns(int, short, void *);
47d2078e3rodrigcvoid    client_dispatch_parent(int, short, void *);
48d2078e3rodrigcvoid    client_shutdown(void);
49d2078e3rodrigcvoid    client_connect(int, short, void *);
50d2078e3rodrigcvoid    client_configure(struct env *);
51d2078e3rodrigcvoid    client_periodic_update(int, short, void *);
52d2078e3rodrigcint	client_build_req(struct idm *, struct idm_req *, struct aldap_message *,
53d2078e3rodrigc	    int, int);
54d2078e3rodrigcint	client_search_idm(struct env *, struct idm *, struct aldap *,
55d2078e3rodrigc	    char **, char *, int, int, enum imsg_type);
56d2078e3rodrigcint	client_try_idm(struct env *, struct idm *);
57d2078e3rodrigcint	client_addr_init(struct idm *);
58d2078e3rodrigcint	client_addr_free(struct idm *);
59d2078e3rodrigc
607e0cb41araujostruct aldap	*client_aldap_open(struct ypldap_addr_list *);
61d2078e3rodrigc
62d2078e3rodrigc/*
63d2078e3rodrigc * dummy wrapper to provide aldap_init with its fd's.
64d2078e3rodrigc */
65d2078e3rodrigcstruct aldap *
667e0cb41araujoclient_aldap_open(struct ypldap_addr_list *addr)
67d2078e3rodrigc{
68d2078e3rodrigc	int			 fd = -1;
69d2078e3rodrigc	struct ypldap_addr	 *p;
70d2078e3rodrigc
717e0cb41araujo	TAILQ_FOREACH(p, addr, next) {
72d2078e3rodrigc		char			 hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
73d2078e3rodrigc		struct sockaddr		*sa = (struct sockaddr *)&p->ss;
74d2078e3rodrigc
7568b3fb6rodrigc		if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf,
76d2078e3rodrigc			sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV))
77d2078e3rodrigc				errx(1, "could not get numeric hostname");
78d2078e3rodrigc
79d2078e3rodrigc		if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
80d2078e3rodrigc			return NULL;
81d2078e3rodrigc
8268b3fb6rodrigc		if (connect(fd, sa, sa->sa_len) == 0)
83d2078e3rodrigc			break;
84d2078e3rodrigc
85d2078e3rodrigc		warn("connect to %s port %s (%s) failed", hbuf, sbuf, "tcp");
86d2078e3rodrigc		close(fd);
87d2078e3rodrigc	}
88d2078e3rodrigc
89d2078e3rodrigc	if (fd == -1)
90d2078e3rodrigc		return NULL;
91d2078e3rodrigc
92d2078e3rodrigc	return aldap_init(fd);
93d2078e3rodrigc}
94d2078e3rodrigc
95d2078e3rodrigcint
96d2078e3rodrigcclient_addr_init(struct idm *idm)
97d2078e3rodrigc{
98d2078e3rodrigc        struct sockaddr_in      *sa_in;
99d2078e3rodrigc        struct sockaddr_in6     *sa_in6;
100d2078e3rodrigc        struct ypldap_addr         *h;
101d2078e3rodrigc
1027e0cb41araujo	TAILQ_FOREACH(h, &idm->idm_addr, next) {
103d2078e3rodrigc                switch (h->ss.ss_family) {
104d2078e3rodrigc                case AF_INET:
105d2078e3rodrigc                        sa_in = (struct sockaddr_in *)&h->ss;
106d2078e3rodrigc                        if (ntohs(sa_in->sin_port) == 0)
107d2078e3rodrigc                                sa_in->sin_port = htons(LDAP_PORT);
108d2078e3rodrigc                        idm->idm_state = STATE_DNS_DONE;
109d2078e3rodrigc                        break;
110d2078e3rodrigc                case AF_INET6:
111d2078e3rodrigc                        sa_in6 = (struct sockaddr_in6 *)&h->ss;
112d2078e3rodrigc                        if (ntohs(sa_in6->sin6_port) == 0)
113d2078e3rodrigc                                sa_in6->sin6_port = htons(LDAP_PORT);
114d2078e3rodrigc                        idm->idm_state = STATE_DNS_DONE;
115d2078e3rodrigc                        break;
116d2078e3rodrigc                default:
117d2078e3rodrigc                        fatalx("king bula sez: wrong AF in client_addr_init");
118d2078e3rodrigc                        /* not reached */
119d2078e3rodrigc                }
120d2078e3rodrigc        }
121d2078e3rodrigc
122d2078e3rodrigc        return (0);
123d2078e3rodrigc}
124d2078e3rodrigc
125d2078e3rodrigcint
126d2078e3rodrigcclient_addr_free(struct idm *idm)
127d2078e3rodrigc{
1287e0cb41araujo        struct ypldap_addr         *h;
129d2078e3rodrigc
1307e0cb41araujo	while (!TAILQ_EMPTY(&idm->idm_addr)) {
1317e0cb41araujo		h = TAILQ_FIRST(&idm->idm_addr);
1327e0cb41araujo		TAILQ_REMOVE(&idm->idm_addr, h, next);
133d2078e3rodrigc		free(h);
134d2078e3rodrigc	}
135d2078e3rodrigc
136d2078e3rodrigc	return (0);
137d2078e3rodrigc}
138d2078e3rodrigc
139d2078e3rodrigcvoid
140d2078e3rodrigcclient_sig_handler(int sig, short event, void *p)
141d2078e3rodrigc{
142d2078e3rodrigc	switch (sig) {
143d2078e3rodrigc	case SIGINT:
144d2078e3rodrigc	case SIGTERM:
145d2078e3rodrigc		client_shutdown();
146d2078e3rodrigc		break;
147d2078e3rodrigc	default:
148d2078e3rodrigc		fatalx("unexpected signal");
149d2078e3rodrigc	}
150d2078e3rodrigc}
151d2078e3rodrigc
152d2078e3rodrigcvoid
153d2078e3rodrigcclient_dispatch_dns(int fd, short events, void *p)
154d2078e3rodrigc{
155d2078e3rodrigc	struct imsg		 imsg;
156d2078e3rodrigc	u_int16_t		 dlen;
157d2078e3rodrigc	u_char			*data;
158d2078e3rodrigc	struct ypldap_addr	*h;
159d2078e3rodrigc	int			 n, wait_cnt = 0;
160d2078e3rodrigc	struct idm		*idm;
161d2078e3rodrigc	int			 shut = 0;
162d2078e3rodrigc
163d2078e3rodrigc	struct env		*env = p;
164d2078e3rodrigc	struct imsgev		*iev = env->sc_iev_dns;
165d2078e3rodrigc	struct imsgbuf		*ibuf = &iev->ibuf;
166d2078e3rodrigc
167d2078e3rodrigc	if ((events & (EV_READ | EV_WRITE)) == 0)
168d2078e3rodrigc		fatalx("unknown event");
169d2078e3rodrigc
170d2078e3rodrigc	if (events & EV_READ) {
17199a0984araujo		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
172d2078e3rodrigc			fatal("imsg_read error");
173d2078e3rodrigc		if (n == 0)
174d2078e3rodrigc			shut = 1;
175d2078e3rodrigc	}
176d2078e3rodrigc	if (events & EV_WRITE) {
177d2078e3rodrigc		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
178d2078e3rodrigc			fatal("msgbuf_write");
179d2078e3rodrigc		if (n == 0)
180d2078e3rodrigc			shut = 1;
181d2078e3rodrigc		goto done;
182d2078e3rodrigc	}
183d2078e3rodrigc
184d2078e3rodrigc	for (;;) {
185d2078e3rodrigc		if ((n = imsg_get(ibuf, &imsg)) == -1)
186d2078e3rodrigc			fatal("client_dispatch_dns: imsg_get error");
187d2078e3rodrigc		if (n == 0)
188d2078e3rodrigc			break;
189d2078e3rodrigc
190d2078e3rodrigc		switch (imsg.hdr.type) {
191d2078e3rodrigc		case IMSG_HOST_DNS:
192d2078e3rodrigc			TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
193d2078e3rodrigc				if (idm->idm_id == imsg.hdr.peerid)
194d2078e3rodrigc					break;
195d2078e3rodrigc			if (idm == NULL) {
196d2078e3rodrigc				log_warnx("IMSG_HOST_DNS with invalid peerID");
197d2078e3rodrigc				break;
198d2078e3rodrigc			}
1997e0cb41araujo			if (!TAILQ_EMPTY(&idm->idm_addr)) {
2007e0cb41araujo				log_warnx("IMSG_HOST_DNS but addrs set!");
201d2078e3rodrigc				break;
202d2078e3rodrigc			}
203d2078e3rodrigc
204d2078e3rodrigc			dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
205d2078e3rodrigc			if (dlen == 0) {	/* no data -> temp error */
206d2078e3rodrigc				idm->idm_state = STATE_DNS_TEMPFAIL;
207d2078e3rodrigc				break;
208d2078e3rodrigc			}
209d2078e3rodrigc
210d2078e3rodrigc			data = (u_char *)imsg.data;
211d2078e3rodrigc			while (dlen >= sizeof(struct sockaddr_storage)) {
2127e0cb41araujo				if ((h = calloc(1, sizeof(*h))) == NULL)
213d2078e3rodrigc					fatal(NULL);
214d2078e3rodrigc				memcpy(&h->ss, data, sizeof(h->ss));
2157e0cb41araujo				TAILQ_INSERT_HEAD(&idm->idm_addr, h, next);
216d2078e3rodrigc
217d2078e3rodrigc				data += sizeof(h->ss);
218d2078e3rodrigc				dlen -= sizeof(h->ss);
219d2078e3rodrigc			}
220d2078e3rodrigc			if (dlen != 0)
221d2078e3rodrigc				fatalx("IMSG_HOST_DNS: dlen != 0");
222d2078e3rodrigc
223d2078e3rodrigc			client_addr_init(idm);
224d2078e3rodrigc
225d2078e3rodrigc			break;
226d2078e3rodrigc		default:
227d2078e3rodrigc			break;
228d2078e3rodrigc		}
229d2078e3rodrigc		imsg_free(&imsg);
230d2078e3rodrigc	}
231d2078e3rodrigc
232d2078e3rodrigc	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
233d2078e3rodrigc		if (client_try_idm(env, idm) == -1)
234d2078e3rodrigc			idm->idm_state = STATE_LDAP_FAIL;
235d2078e3rodrigc
236d2078e3rodrigc		if (idm->idm_state < STATE_LDAP_DONE)
237d2078e3rodrigc			wait_cnt++;
238d2078e3rodrigc	}
239d2078e3rodrigc	if (wait_cnt == 0)
240d2078e3rodrigc		imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1,
241d2078e3rodrigc		    NULL, 0);
242d2078e3rodrigc
243d2078e3rodrigcdone:
244d2078e3rodrigc	if (!shut)
245d2078e3rodrigc		imsg_event_add(iev);
246d2078e3rodrigc	else {
247d2078e3rodrigc		/* this pipe is dead, so remove the event handler */
248d2078e3rodrigc		event_del(&iev->ev);
249d2078e3rodrigc		event_loopexit(NULL);
250d2078e3rodrigc	}
251d2078e3rodrigc}
252d2078e3rodrigc
253d2078e3rodrigcvoid
254d2078e3rodrigcclient_dispatch_parent(int fd, short events, void *p)
255d2078e3rodrigc{
256d2078e3rodrigc	int			 n;
257d2078e3rodrigc	int			 shut = 0;
258d2078e3rodrigc	struct imsg		 imsg;
259d2078e3rodrigc	struct env		*env = p;
260d2078e3rodrigc	struct imsgev		*iev = env->sc_iev;
261d2078e3rodrigc	struct imsgbuf		*ibuf = &iev->ibuf;
262d2078e3rodrigc
263d2078e3rodrigc	if ((events & (EV_READ | EV_WRITE)) == 0)
264d2078e3rodrigc		fatalx("unknown event");
265d2078e3rodrigc
266d2078e3rodrigc	if (events & EV_READ) {
26799a0984araujo		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
268d2078e3rodrigc			fatal("imsg_read error");
269d2078e3rodrigc		if (n == 0)
270d2078e3rodrigc			shut = 1;
271d2078e3rodrigc	}
272d2078e3rodrigc	if (events & EV_WRITE) {
273d2078e3rodrigc		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
274d2078e3rodrigc			fatal("msgbuf_write");
275d2078e3rodrigc		if (n == 0)
276d2078e3rodrigc			shut = 1;
277d2078e3rodrigc		goto done;
278d2078e3rodrigc	}
279d2078e3rodrigc
280d2078e3rodrigc	for (;;) {
281d2078e3rodrigc		if ((n = imsg_get(ibuf, &imsg)) == -1)
282d2078e3rodrigc			fatal("client_dispatch_parent: imsg_get error");
283d2078e3rodrigc		if (n == 0)
284d2078e3rodrigc			break;
285d2078e3rodrigc
286d2078e3rodrigc		switch (imsg.hdr.type) {
287d2078e3rodrigc		case IMSG_CONF_START: {
288d2078e3rodrigc			struct env	params;
289d2078e3rodrigc
290d2078e3rodrigc			if (env->sc_flags & F_CONFIGURING) {
291d2078e3rodrigc				log_warnx("configuration already in progress");
292d2078e3rodrigc				break;
293d2078e3rodrigc			}
294d2078e3rodrigc			memcpy(&params, imsg.data, sizeof(params));
295d2078e3rodrigc			log_debug("configuration starting");
296d2078e3rodrigc			env->sc_flags |= F_CONFIGURING;
297d2078e3rodrigc			purge_config(env);
298d2078e3rodrigc			memcpy(&env->sc_conf_tv, &params.sc_conf_tv,
299d2078e3rodrigc			    sizeof(env->sc_conf_tv));
300d2078e3rodrigc			env->sc_flags |= params.sc_flags;
301d2078e3rodrigc			break;
302d2078e3rodrigc		}
303d2078e3rodrigc		case IMSG_CONF_IDM: {
304d2078e3rodrigc			struct idm	*idm;
305d2078e3rodrigc
306d2078e3rodrigc			if (!(env->sc_flags & F_CONFIGURING))
307d2078e3rodrigc				break;
308d2078e3rodrigc			if ((idm = calloc(1, sizeof(*idm))) == NULL)
309d2078e3rodrigc				fatal(NULL);
310d2078e3rodrigc			memcpy(idm, imsg.data, sizeof(*idm));
311d2078e3rodrigc			idm->idm_env = env;
312d2078e3rodrigc			TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry);
313d2078e3rodrigc			break;
314d2078e3rodrigc		}
315d2078e3rodrigc		case IMSG_CONF_END:
316d2078e3rodrigc			env->sc_flags &= ~F_CONFIGURING;
317d2078e3rodrigc			log_debug("applying configuration");
318d2078e3rodrigc			client_configure(env);
319d2078e3rodrigc			break;
320d2078e3rodrigc		default:
321d2078e3rodrigc			log_debug("client_dispatch_parent: unexpect imsg %d",
322d2078e3rodrigc			    imsg.hdr.type);
323d2078e3rodrigc
324d2078e3rodrigc			break;
325d2078e3rodrigc		}
326d2078e3rodrigc		imsg_free(&imsg);
327d2078e3rodrigc	}
328d2078e3rodrigc
329d2078e3rodrigcdone:
330d2078e3rodrigc	if (!shut)
331d2078e3rodrigc		imsg_event_add(iev);
332d2078e3rodrigc	else {
333d2078e3rodrigc		/* this pipe is dead, so remove the event handler */
334d2078e3rodrigc		event_del(&iev->ev);
335d2078e3rodrigc		event_loopexit(NULL);
336d2078e3rodrigc	}
337d2078e3rodrigc}
338d2078e3rodrigc
339d2078e3rodrigcvoid
340d2078e3rodrigcclient_shutdown(void)
341d2078e3rodrigc{
342d2078e3rodrigc	log_info("ldap client exiting");
343d2078e3rodrigc	_exit(0);
344d2078e3rodrigc}
345d2078e3rodrigc
346d2078e3rodrigcpid_t
347d2078e3rodrigcldapclient(int pipe_main2client[2])
348d2078e3rodrigc{
349d2078e3rodrigc	pid_t            pid, dns_pid;
350d2078e3rodrigc	int              pipe_dns[2];
351d2078e3rodrigc	struct passwd	*pw;
352d2078e3rodrigc	struct event	 ev_sigint;
353d2078e3rodrigc	struct event	 ev_sigterm;
354d2078e3rodrigc	struct env	 env;
355d2078e3rodrigc
356d2078e3rodrigc	switch (pid = fork()) {
357d2078e3rodrigc	case -1:
358d2078e3rodrigc		fatal("cannot fork");
359d2078e3rodrigc		break;
360d2078e3rodrigc	case 0:
361d2078e3rodrigc		break;
362d2078e3rodrigc	default:
363d2078e3rodrigc		return (pid);
364d2078e3rodrigc	}
365d2078e3rodrigc
366d2078e3rodrigc	bzero(&env, sizeof(env));
367d2078e3rodrigc	TAILQ_INIT(&env.sc_idms);
368d2078e3rodrigc
3692dd2581araujo	if ((pw = getpwnam(YPLDAP_USER)) == NULL)
370d2078e3rodrigc		fatal("getpwnam");
371d2078e3rodrigc
372d2078e3rodrigc	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dns) == -1)
373d2078e3rodrigc		fatal("socketpair");
374d2078e3rodrigc	dns_pid = ypldap_dns(pipe_dns, pw);
375d2078e3rodrigc	close(pipe_dns[1]);
376d2078e3rodrigc
377d2078e3rodrigc#ifndef DEBUG
378d2078e3rodrigc	if (chroot(pw->pw_dir) == -1)
379d2078e3rodrigc		fatal("chroot");
380d2078e3rodrigc	if (chdir("/") == -1)
381d2078e3rodrigc		fatal("chdir");
382d2078e3rodrigc#else
383d2078e3rodrigc#warning disabling chrooting in DEBUG mode
384d2078e3rodrigc#endif
385d2078e3rodrigc	setproctitle("ldap client");
386d2078e3rodrigc	ypldap_process = PROC_CLIENT;
387d2078e3rodrigc
388d2078e3rodrigc#ifndef DEBUG
389d2078e3rodrigc	if (setgroups(1, &pw->pw_gid) ||
390d2078e3rodrigc	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
391d2078e3rodrigc	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
392d2078e3rodrigc		fatal("cannot drop privileges");
393d2078e3rodrigc#else
394d2078e3rodrigc#warning disabling privilege revocation in DEBUG mode
395d2078e3rodrigc#endif
396d2078e3rodrigc
397d2078e3rodrigc	event_init();
398d2078e3rodrigc	signal(SIGPIPE, SIG_IGN);
399d2078e3rodrigc	signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
400d2078e3rodrigc	signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
401d2078e3rodrigc	signal_add(&ev_sigint, NULL);
402d2078e3rodrigc	signal_add(&ev_sigterm, NULL);
403d2078e3rodrigc
404d2078e3rodrigc	close(pipe_main2client[0]);
405d2078e3rodrigc	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
406d2078e3rodrigc		fatal(NULL);
407d2078e3rodrigc	if ((env.sc_iev_dns = calloc(1, sizeof(*env.sc_iev_dns))) == NULL)
408d2078e3rodrigc		fatal(NULL);
409d2078e3rodrigc
410d2078e3rodrigc	env.sc_iev->events = EV_READ;
411d2078e3rodrigc	env.sc_iev->data = &env;
412d2078e3rodrigc	imsg_init(&env.sc_iev->ibuf, pipe_main2client[1]);
413d2078e3rodrigc	env.sc_iev->handler = client_dispatch_parent;
414d2078e3rodrigc	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
415d2078e3rodrigc	    env.sc_iev->handler, &env);
416d2078e3rodrigc	event_add(&env.sc_iev->ev, NULL);
417d2078e3rodrigc
418d2078e3rodrigc	env.sc_iev_dns->events = EV_READ;
419d2078e3rodrigc	env.sc_iev_dns->data = &env;
420d2078e3rodrigc	imsg_init(&env.sc_iev_dns->ibuf, pipe_dns[0]);
421d2078e3rodrigc	env.sc_iev_dns->handler = client_dispatch_dns;
422d2078e3rodrigc	event_set(&env.sc_iev_dns->ev, env.sc_iev_dns->ibuf.fd,
423d2078e3rodrigc	    env.sc_iev_dns->events, env.sc_iev_dns->handler, &env);
424d2078e3rodrigc	event_add(&env.sc_iev_dns->ev, NULL);
425d2078e3rodrigc
426d2078e3rodrigc	event_dispatch();
427d2078e3rodrigc	client_shutdown();
428d2078e3rodrigc
429d2078e3rodrigc	return (0);
430d2078e3rodrigc
431d2078e3rodrigc}
432d2078e3rodrigc
433d2078e3rodrigcint
434d2078e3rodrigcclient_build_req(struct idm *idm, struct idm_req *ir, struct aldap_message *m,
435d2078e3rodrigc    int min_attr, int max_attr)
436d2078e3rodrigc{
437d2078e3rodrigc	char	**ldap_attrs;
438d2078e3rodrigc	int	 i, k;
439d2078e3rodrigc
440d2078e3rodrigc	bzero(ir, sizeof(*ir));
441d2078e3rodrigc	for (i = min_attr; i < max_attr; i++) {
442d2078e3rodrigc		if (idm->idm_flags & F_FIXED_ATTR(i)) {
443d2078e3rodrigc			if (strlcat(ir->ir_line, idm->idm_attrs[i],
444d2078e3rodrigc			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
445d2078e3rodrigc				/*
446d2078e3rodrigc				 * entry yields a line > 1024, trash it.
447d2078e3rodrigc				 */
448d2078e3rodrigc				return (-1);
449d2078e3rodrigc
450d2078e3rodrigc			if (i == ATTR_UID) {
451d2078e3rodrigc				ir->ir_key.ik_uid = strtonum(
452d2078e3rodrigc				    idm->idm_attrs[i], 0,
453d2078e3rodrigc				    UID_MAX, NULL);
454d2078e3rodrigc			} else if (i == ATTR_GR_GID) {
455d2078e3rodrigc				ir->ir_key.ik_gid = strtonum(
456d2078e3rodrigc				    idm->idm_attrs[i], 0,
457d2078e3rodrigc				    GID_MAX, NULL);
458d2078e3rodrigc			}
459d2078e3rodrigc		} else if (idm->idm_list & F_LIST(i)) {
460d2078e3rodrigc			aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs);
461d2078e3rodrigc			for (k = 0; k >= 0 && ldap_attrs && ldap_attrs[k] != NULL; k++) {
462d2078e3rodrigc				/* XXX: Fail when attributes have illegal characters e.g. ',' */
463d2078e3rodrigc				if (strlcat(ir->ir_line, ldap_attrs[k],
464d2078e3rodrigc				    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
465d2078e3rodrigc					continue;
466d2078e3rodrigc				if (ldap_attrs[k+1] != NULL)
467d2078e3rodrigc					if (strlcat(ir->ir_line, ",",
468d2078e3rodrigc						    sizeof(ir->ir_line))
469d2078e3rodrigc					    >= sizeof(ir->ir_line)) {
470d2078e3rodrigc						aldap_free_attr(ldap_attrs);
471d2078e3rodrigc						return (-1);
472d2078e3rodrigc					}
473d2078e3rodrigc			}
474d2078e3rodrigc			aldap_free_attr(ldap_attrs);
475d2078e3rodrigc		} else {
476d2078e3rodrigc			if (aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs) == -1)
477d2078e3rodrigc				return (-1);
478d2078e3rodrigc			if (ldap_attrs[0] == NULL)
479d2078e3rodrigc				return (-1);
480d2078e3rodrigc			if (strlcat(ir->ir_line, ldap_attrs[0],
481d2078e3rodrigc			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) {
482d2078e3rodrigc				aldap_free_attr(ldap_attrs);
483d2078e3rodrigc				return (-1);
484d2078e3rodrigc			}
485d2078e3rodrigc			if (i == ATTR_UID) {
486d2078e3rodrigc				ir->ir_key.ik_uid = strtonum(
487d2078e3rodrigc				    ldap_attrs[0], 0, UID_MAX, NULL);
488d2078e3rodrigc			} else if (i == ATTR_GR_GID) {
489d2078e3rodrigc				ir->ir_key.ik_uid = strtonum(
490d2078e3rodrigc				    ldap_attrs[0], 0, GID_MAX, NULL);
491d2078e3rodrigc			}
492d2078e3rodrigc			aldap_free_attr(ldap_attrs);
493d2078e3rodrigc		}
494d2078e3rodrigc
495d2078e3rodrigc		if (i + 1 != max_attr)
496d2078e3rodrigc			if (strlcat(ir->ir_line, ":",
497d2078e3rodrigc			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
498d2078e3rodrigc				return (-1);
499d2078e3rodrigc	}
500d2078e3rodrigc
501d2078e3rodrigc	return (0);
502d2078e3rodrigc}
503d2078e3rodrigc
504d2078e3rodrigcint
505d2078e3rodrigcclient_search_idm(struct env *env, struct idm *idm, struct aldap *al,
506d2078e3rodrigc    char **attrs, char *filter, int min_attr, int max_attr,
507d2078e3rodrigc    enum imsg_type type)
508d2078e3rodrigc{
509d2078e3rodrigc	struct idm_req		 ir;
510d2078e3rodrigc	struct aldap_message	*m;
511d2078e3rodrigc	struct aldap_page_control *pg = NULL;
512d2078e3rodrigc	const char		*errstr;
513d2078e3rodrigc	char			*dn;
514d2078e3rodrigc
515d2078e3rodrigc	dn = idm->idm_basedn;
516d2078e3rodrigc	if (type == IMSG_GRP_ENTRY && idm->idm_groupdn[0] != '\0')
517d2078e3rodrigc		dn = idm->idm_groupdn;
518d2078e3rodrigc
519d2078e3rodrigc	do {
520d2078e3rodrigc		if (aldap_search(al, dn, LDAP_SCOPE_SUBTREE,
521d2078e3rodrigc		    filter, attrs, 0, 0, 0, pg) == -1) {
522d2078e3rodrigc			aldap_get_errno(al, &errstr);
523d2078e3rodrigc			log_debug("%s", errstr);
524d2078e3rodrigc			return (-1);
525d2078e3rodrigc		}
526d2078e3rodrigc
527d2078e3rodrigc		if (pg != NULL) {
528d2078e3rodrigc			aldap_freepage(pg);
529d2078e3rodrigc			pg = NULL;
530d2078e3rodrigc		}
531d2078e3rodrigc
532d2078e3rodrigc		while ((m = aldap_parse(al)) != NULL) {
533d2078e3rodrigc			if (al->msgid != m->msgid) {
534d2078e3rodrigc				goto fail;
535d2078e3rodrigc			}
536d2078e3rodrigc
537d2078e3rodrigc			if (m->message_type == LDAP_RES_SEARCH_RESULT) {
538d2078e3rodrigc				if (m->page != NULL && m->page->cookie_len != 0)
539d2078e3rodrigc					pg = m->page;
540d2078e3rodrigc				else
541d2078e3rodrigc					pg = NULL;
542d2078e3rodrigc
543d2078e3rodrigc				aldap_freemsg(m);
544d2078e3rodrigc				break;
545d2078e3rodrigc			}
546d2078e3rodrigc
547d2078e3rodrigc			if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
548d2078e3rodrigc				goto fail;
549d2078e3rodrigc			}
550d2078e3rodrigc
551d2078e3rodrigc			if (client_build_req(idm, &ir, m, min_attr, max_attr) == 0)
552d2078e3rodrigc				imsg_compose_event(env->sc_iev, type, 0, 0, -1,
553d2078e3rodrigc				    &ir, sizeof(ir));
554d2078e3rodrigc
555d2078e3rodrigc			aldap_freemsg(m);
556d2078e3rodrigc		}
557d2078e3rodrigc	} while (pg != NULL);
558d2078e3rodrigc
559d2078e3rodrigc	return (0);
560d2078e3rodrigc
561d2078e3rodrigcfail:
562d2078e3rodrigc	aldap_freemsg(m);
563d2078e3rodrigc	if (pg != NULL) {
564d2078e3rodrigc		aldap_freepage(pg);
565d2078e3rodrigc	}
566d2078e3rodrigc
567d2078e3rodrigc	return (-1);
568d2078e3rodrigc}
569d2078e3rodrigc
570d2078e3rodrigcint
571d2078e3rodrigcclient_try_idm(struct env *env, struct idm *idm)
572d2078e3rodrigc{
573d2078e3rodrigc	const char		*where;
574d2078e3rodrigc	char			*attrs[ATTR_MAX+1];
575d2078e3rodrigc	int			 i, j;
576d2078e3rodrigc	struct aldap_message	*m;
577d2078e3rodrigc	struct aldap		*al;
578d2078e3rodrigc
579d2078e3rodrigc	where = "connect";
5807e0cb41araujo	if ((al = client_aldap_open(&idm->idm_addr)) == NULL)
581d2078e3rodrigc		return (-1);
582d2078e3rodrigc
583d2078e3rodrigc	if (idm->idm_flags & F_NEEDAUTH) {
584d2078e3rodrigc		where = "binding";
585d2078e3rodrigc		if (aldap_bind(al, idm->idm_binddn, idm->idm_bindcred) == -1)
586d2078e3rodrigc			goto bad;
587d2078e3rodrigc
588d2078e3rodrigc		where = "parsing";
589d2078e3rodrigc		if ((m = aldap_parse(al)) == NULL)
590d2078e3rodrigc			goto bad;
591d2078e3rodrigc		where = "verifying msgid";
592d2078e3rodrigc		if (al->msgid != m->msgid) {
593d2078e3rodrigc			aldap_freemsg(m);
594d2078e3rodrigc			goto bad;
595d2078e3rodrigc		}
596d2078e3rodrigc		aldap_freemsg(m);
597d2078e3rodrigc	}
598d2078e3rodrigc
599d2078e3rodrigc	bzero(attrs, sizeof(attrs));
600d2078e3rodrigc	for (i = 0, j = 0; i < ATTR_MAX; i++) {
601d2078e3rodrigc		if (idm->idm_flags & F_FIXED_ATTR(i))
602d2078e3rodrigc			continue;
603d2078e3rodrigc		attrs[j++] = idm->idm_attrs[i];
604d2078e3rodrigc	}
605d2078e3rodrigc	attrs[j] = NULL;
606d2078e3rodrigc
607d2078e3rodrigc	/*
608d2078e3rodrigc	 * build password line.
609d2078e3rodrigc	 */
610d2078e3rodrigc	where = "search";
611d2078e3rodrigc	log_debug("searching password entries");
612d2078e3rodrigc	if (client_search_idm(env, idm, al, attrs,
613d2078e3rodrigc	    idm->idm_filters[FILTER_USER], 0, ATTR_MAX, IMSG_PW_ENTRY) == -1)
614d2078e3rodrigc		goto bad;
615d2078e3rodrigc
616d2078e3rodrigc	bzero(attrs, sizeof(attrs));
617d2078e3rodrigc	for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) {
618d2078e3rodrigc		if (idm->idm_flags & F_FIXED_ATTR(i))
619d2078e3rodrigc			continue;
620d2078e3rodrigc		attrs[j++] = idm->idm_attrs[i];
621d2078e3rodrigc	}
622d2078e3rodrigc	attrs[j] = NULL;
623d2078e3rodrigc
624d2078e3rodrigc	/*
625d2078e3rodrigc	 * build group line.
626d2078e3rodrigc	 */
627d2078e3rodrigc	where = "search";
628d2078e3rodrigc	log_debug("searching group entries");
629d2078e3rodrigc	if (client_search_idm(env, idm, al, attrs,
630d2078e3rodrigc	    idm->idm_filters[FILTER_GROUP], ATTR_GR_MIN, ATTR_GR_MAX,
631d2078e3rodrigc	    IMSG_GRP_ENTRY) == -1)
632d2078e3rodrigc		goto bad;
633d2078e3rodrigc
634d2078e3rodrigc	aldap_close(al);
635d2078e3rodrigc
636d2078e3rodrigc	idm->idm_state = STATE_LDAP_DONE;
637d2078e3rodrigc
638d2078e3rodrigc	return (0);
639d2078e3rodrigcbad:
640d2078e3rodrigc	aldap_close(al);
641d2078e3rodrigc	log_debug("directory %s errored out in %s", idm->idm_name, where);
642d2078e3rodrigc	return (-1);
643d2078e3rodrigc}
644d2078e3rodrigc
645d2078e3rodrigcvoid
646d2078e3rodrigcclient_periodic_update(int fd, short event, void *p)
647d2078e3rodrigc{
648d2078e3rodrigc	struct env	*env = p;
649d2078e3rodrigc
650d2078e3rodrigc	struct idm	*idm;
651d2078e3rodrigc	int		 fail_cnt = 0;
652d2078e3rodrigc
653d2078e3rodrigc	/* If LDAP isn't finished, notify the master process to trash the
654d2078e3rodrigc	 * update. */
655d2078e3rodrigc	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
656d2078e3rodrigc		if (idm->idm_state < STATE_LDAP_DONE)
657d2078e3rodrigc			fail_cnt++;
658d2078e3rodrigc
659d2078e3rodrigc		idm->idm_state = STATE_NONE;
660d2078e3rodrigc
661d2078e3rodrigc		client_addr_free(idm);
662d2078e3rodrigc	}
663d2078e3rodrigc	if (fail_cnt > 0) {
664d2078e3rodrigc		log_debug("trash the update");
665d2078e3rodrigc		imsg_compose_event(env->sc_iev, IMSG_TRASH_UPDATE, 0, 0, -1,
666d2078e3rodrigc		    NULL, 0);
667d2078e3rodrigc	}
668d2078e3rodrigc
669d2078e3rodrigc	client_configure(env);
670d2078e3rodrigc}
671d2078e3rodrigc
672d2078e3rodrigcvoid
673d2078e3rodrigcclient_configure(struct env *env)
674d2078e3rodrigc{
675d2078e3rodrigc	struct timeval	 tv;
676d2078e3rodrigc	struct idm	*idm;
677d2078e3rodrigc        u_int16_t        dlen;
678d2078e3rodrigc
679d2078e3rodrigc	log_debug("connecting to directories");
680d2078e3rodrigc
681d2078e3rodrigc	imsg_compose_event(env->sc_iev, IMSG_START_UPDATE, 0, 0, -1, NULL, 0);
682d2078e3rodrigc
683d2078e3rodrigc	/* Start the DNS lookups */
684d2078e3rodrigc	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
685d2078e3rodrigc		dlen = strlen(idm->idm_name) + 1;
686d2078e3rodrigc		imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id,
687d2078e3rodrigc		    0, -1, idm->idm_name, dlen);
688d2078e3rodrigc	}
689d2078e3rodrigc
690d2078e3rodrigc	tv.tv_sec = env->sc_conf_tv.tv_sec;
691d2078e3rodrigc	tv.tv_usec = env->sc_conf_tv.tv_usec;
692d2078e3rodrigc	evtimer_set(&env->sc_conf_ev, client_periodic_update, env);
693d2078e3rodrigc	evtimer_add(&env->sc_conf_ev, &tv);
694d2078e3rodrigc}
695