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