xref: /openbsd-src/usr.sbin/ypldap/ldapclient.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /* $OpenBSD: ldapclient.c,v 1.55 2024/11/21 13:38:15 claudio Exp $ */
2f6242408Spyr 
3f6242408Spyr /*
4af720893Saschrijver  * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
5f6242408Spyr  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6f6242408Spyr  *
7f6242408Spyr  * Permission to use, copy, modify, and distribute this software for any
8f6242408Spyr  * purpose with or without fee is hereby granted, provided that the above
9f6242408Spyr  * copyright notice and this permission notice appear in all copies.
10f6242408Spyr  *
11f6242408Spyr  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12f6242408Spyr  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13f6242408Spyr  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14f6242408Spyr  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15f6242408Spyr  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16f6242408Spyr  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17f6242408Spyr  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f6242408Spyr  */
19f6242408Spyr 
20f6242408Spyr #include <sys/types.h>
21f6242408Spyr #include <sys/queue.h>
22f6242408Spyr #include <sys/socket.h>
23f6242408Spyr #include <sys/tree.h>
24f6242408Spyr 
25f6242408Spyr #include <netinet/in.h>
26f6242408Spyr #include <arpa/inet.h>
27f6242408Spyr 
28af720893Saschrijver #include <netdb.h>
29f6242408Spyr #include <errno.h>
30af720893Saschrijver #include <err.h>
31e83a889dSderaadt #include <signal.h>
32f6242408Spyr #include <event.h>
33f6242408Spyr #include <fcntl.h>
34f6242408Spyr #include <unistd.h>
35f6242408Spyr #include <pwd.h>
36f6242408Spyr #include <stdio.h>
37f6242408Spyr #include <stdlib.h>
38f6242408Spyr #include <string.h>
39b9fc9a72Sderaadt #include <limits.h>
40f6242408Spyr 
41af720893Saschrijver #include "aldap.h"
420be9c890Sbenno #include "log.h"
43f6242408Spyr #include "ypldap.h"
44f6242408Spyr 
45f6242408Spyr void    client_sig_handler(int, short, void *);
46db474592Saschrijver void	client_dispatch_dns(int, short, void *);
47f6242408Spyr void    client_dispatch_parent(int, short, void *);
48f6242408Spyr void    client_shutdown(void);
49f6242408Spyr void    client_configure(struct env *);
50db474592Saschrijver void    client_periodic_update(int, short, void *);
5168e6d159Smartinh int	client_build_req(struct idm *, struct idm_req *, struct aldap_message *,
5268e6d159Smartinh 	    int, int);
53bb704a6bSmartinh int	client_search_idm(struct env *, struct idm *, struct aldap *,
54bb704a6bSmartinh 	    char **, char *, int, int, enum imsg_type);
5534d9787bSjmatthew int	client_try_idm(struct env *, struct idm *, struct ypldap_addr *);
568adff840Szhuk void	client_addr_init(struct idm *);
57db474592Saschrijver int	client_addr_free(struct idm *);
58f6242408Spyr 
598adff840Szhuk void
60db474592Saschrijver client_addr_init(struct idm *idm)
61db474592Saschrijver {
62db474592Saschrijver         struct sockaddr_in      *sa_in;
63db474592Saschrijver         struct sockaddr_in6     *sa_in6;
64db474592Saschrijver         struct ypldap_addr      *h;
6532814761Sjmatthew 	int                     defport;
6632814761Sjmatthew 
6732814761Sjmatthew 	if (idm->idm_port != 0)
6832814761Sjmatthew 		defport = idm->idm_port;
6932814761Sjmatthew 	else if (idm->idm_flags & F_SSL)
7032814761Sjmatthew 		defport = LDAPS_PORT;
7132814761Sjmatthew 	else
7232814761Sjmatthew 		defport = LDAP_PORT;
73db474592Saschrijver 
74441150e0Sjmatthew 	TAILQ_FOREACH(h, &idm->idm_addr, next) {
75db474592Saschrijver                 switch (h->ss.ss_family) {
76db474592Saschrijver                 case AF_INET:
77db474592Saschrijver                         sa_in = (struct sockaddr_in *)&h->ss;
78db474592Saschrijver                         if (ntohs(sa_in->sin_port) == 0)
7932814761Sjmatthew                                 sa_in->sin_port = htons(defport);
80db474592Saschrijver                         idm->idm_state = STATE_DNS_DONE;
81db474592Saschrijver                         break;
82db474592Saschrijver                 case AF_INET6:
83db474592Saschrijver                         sa_in6 = (struct sockaddr_in6 *)&h->ss;
84db474592Saschrijver                         if (ntohs(sa_in6->sin6_port) == 0)
8532814761Sjmatthew                                 sa_in6->sin6_port = htons(defport);
86db474592Saschrijver                         idm->idm_state = STATE_DNS_DONE;
87db474592Saschrijver                         break;
88db474592Saschrijver                 default:
89db474592Saschrijver                         fatalx("king bula sez: wrong AF in client_addr_init");
90db474592Saschrijver                         /* not reached */
91db474592Saschrijver                 }
92db474592Saschrijver         }
93db474592Saschrijver }
94db474592Saschrijver 
95db474592Saschrijver int
96db474592Saschrijver client_addr_free(struct idm *idm)
97db474592Saschrijver {
98441150e0Sjmatthew         struct ypldap_addr	*h;
99db474592Saschrijver 
100441150e0Sjmatthew 	while (!TAILQ_EMPTY(&idm->idm_addr)) {
101441150e0Sjmatthew 		h = TAILQ_FIRST(&idm->idm_addr);
102441150e0Sjmatthew 		TAILQ_REMOVE(&idm->idm_addr, h, next);
103db474592Saschrijver 		free(h);
1046f89f0cdSzinovik 	}
105db474592Saschrijver 
106db474592Saschrijver 	return (0);
107db474592Saschrijver }
1086106e71cSaschrijver 
109f6242408Spyr void
110f6242408Spyr client_sig_handler(int sig, short event, void *p)
111f6242408Spyr {
112f6242408Spyr 	switch (sig) {
113f6242408Spyr 	case SIGINT:
114f6242408Spyr 	case SIGTERM:
115f6242408Spyr 		client_shutdown();
116f6242408Spyr 		break;
11773492e0cSclaudio 	case SIGHUP:
11873492e0cSclaudio 		/* ingore */
11973492e0cSclaudio 		break;
120f6242408Spyr 	default:
121f6242408Spyr 		fatalx("unexpected signal");
122f6242408Spyr 	}
123f6242408Spyr }
124f6242408Spyr 
125f6242408Spyr void
126f485ae0dSkrw client_dispatch_dns(int fd, short events, void *p)
127db474592Saschrijver {
128db474592Saschrijver 	struct imsg		 imsg;
129db474592Saschrijver 	u_int16_t		 dlen;
130db474592Saschrijver 	u_char			*data;
131db474592Saschrijver 	struct ypldap_addr	*h;
132db474592Saschrijver 	int			 n, wait_cnt = 0;
133db474592Saschrijver 	struct idm		*idm;
134db474592Saschrijver 	int			 shut = 0;
135db474592Saschrijver 
136db474592Saschrijver 	struct env		*env = p;
137d46aeb19Seric 	struct imsgev		*iev = env->sc_iev_dns;
138d46aeb19Seric 	struct imsgbuf		*ibuf = &iev->ibuf;
139db474592Saschrijver 
140f485ae0dSkrw 	if ((events & (EV_READ | EV_WRITE)) == 0)
141f485ae0dSkrw 		fatalx("unknown event");
142f485ae0dSkrw 
143f485ae0dSkrw 	if (events & EV_READ) {
144668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
145dd7efffeSclaudio 			fatal("imsgbuf_read error");
146db474592Saschrijver 		if (n == 0)
147db474592Saschrijver 			shut = 1;
148f485ae0dSkrw 	}
149f485ae0dSkrw 	if (events & EV_WRITE) {
150dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
151c1aa9554Sclaudio 			if (errno == EPIPE)	/* connection closed */
15242a8b0bcSkrw 				shut = 1;
153c1aa9554Sclaudio 			else
154dd7efffeSclaudio 				fatal("imsgbuf_write");
155c1aa9554Sclaudio 		}
156db474592Saschrijver 	}
157db474592Saschrijver 
158db474592Saschrijver 	for (;;) {
159db474592Saschrijver 		if ((n = imsg_get(ibuf, &imsg)) == -1)
160fdca1675Szinovik 			fatal("client_dispatch_dns: imsg_get error");
161db474592Saschrijver 		if (n == 0)
162db474592Saschrijver 			break;
163db474592Saschrijver 
164db474592Saschrijver 		switch (imsg.hdr.type) {
165db474592Saschrijver 		case IMSG_HOST_DNS:
166db474592Saschrijver 			TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
167db474592Saschrijver 				if (idm->idm_id == imsg.hdr.peerid)
168db474592Saschrijver 					break;
169db474592Saschrijver 			if (idm == NULL) {
170db474592Saschrijver 				log_warnx("IMSG_HOST_DNS with invalid peerID");
171db474592Saschrijver 				break;
172db474592Saschrijver 			}
173441150e0Sjmatthew 			if (!TAILQ_EMPTY(&idm->idm_addr)) {
174441150e0Sjmatthew 				log_warnx("IMSG_HOST_DNS but addrs set!");
175db474592Saschrijver 				break;
176db474592Saschrijver 			}
177db474592Saschrijver 
178db474592Saschrijver 			dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
179db474592Saschrijver 			if (dlen == 0) {	/* no data -> temp error */
180db474592Saschrijver 				idm->idm_state = STATE_DNS_TEMPFAIL;
181db474592Saschrijver 				break;
182db474592Saschrijver 			}
183db474592Saschrijver 
184db474592Saschrijver 			data = (u_char *)imsg.data;
185db474592Saschrijver 			while (dlen >= sizeof(struct sockaddr_storage)) {
186441150e0Sjmatthew 				if ((h = calloc(1, sizeof(*h))) == NULL)
187db474592Saschrijver 					fatal(NULL);
188db474592Saschrijver 				memcpy(&h->ss, data, sizeof(h->ss));
189441150e0Sjmatthew 				TAILQ_INSERT_HEAD(&idm->idm_addr, h, next);
190db474592Saschrijver 
191db474592Saschrijver 				data += sizeof(h->ss);
192db474592Saschrijver 				dlen -= sizeof(h->ss);
193db474592Saschrijver 			}
194db474592Saschrijver 			if (dlen != 0)
195db474592Saschrijver 				fatalx("IMSG_HOST_DNS: dlen != 0");
196db474592Saschrijver 
197db474592Saschrijver 			client_addr_init(idm);
198db474592Saschrijver 
199db474592Saschrijver 			break;
200db474592Saschrijver 		default:
201db474592Saschrijver 			break;
202db474592Saschrijver 		}
203db474592Saschrijver 		imsg_free(&imsg);
204db474592Saschrijver 	}
205db474592Saschrijver 
206db474592Saschrijver 	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
20734d9787bSjmatthew 		TAILQ_FOREACH(h, &idm->idm_addr, next) {
20834d9787bSjmatthew 			if (client_try_idm(env, idm, h) == -1)
209db474592Saschrijver 				idm->idm_state = STATE_LDAP_FAIL;
21034d9787bSjmatthew 			else
21134d9787bSjmatthew 				break;
21234d9787bSjmatthew 		}
213db474592Saschrijver 
214db474592Saschrijver 		if (idm->idm_state < STATE_LDAP_DONE)
215db474592Saschrijver 			wait_cnt++;
216db474592Saschrijver 	}
217db474592Saschrijver 	if (wait_cnt == 0)
218d46aeb19Seric 		imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1,
219d46aeb19Seric 		    NULL, 0);
220db474592Saschrijver 
221db474592Saschrijver 	if (!shut)
222d46aeb19Seric 		imsg_event_add(iev);
223db474592Saschrijver 	else {
224db474592Saschrijver 		/* this pipe is dead, so remove the event handler */
225d46aeb19Seric 		event_del(&iev->ev);
226db474592Saschrijver 		event_loopexit(NULL);
227db474592Saschrijver 	}
228db474592Saschrijver }
229db474592Saschrijver 
230db474592Saschrijver void
231f485ae0dSkrw client_dispatch_parent(int fd, short events, void *p)
232f6242408Spyr {
233f6242408Spyr 	int			 n;
234f6242408Spyr 	int			 shut = 0;
235f6242408Spyr 	struct imsg		 imsg;
236f6242408Spyr 	struct env		*env = p;
237d46aeb19Seric 	struct imsgev		*iev = env->sc_iev;
238d46aeb19Seric 	struct imsgbuf		*ibuf = &iev->ibuf;
239f6242408Spyr 
240f485ae0dSkrw 	if ((events & (EV_READ | EV_WRITE)) == 0)
241f485ae0dSkrw 		fatalx("unknown event");
242f6242408Spyr 
243f485ae0dSkrw 	if (events & EV_READ) {
244668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
245dd7efffeSclaudio 			fatal("imsgbuf_read error");
246f6242408Spyr 		if (n == 0)
247f6242408Spyr 			shut = 1;
248f485ae0dSkrw 	}
249f485ae0dSkrw 	if (events & EV_WRITE) {
250dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
251c1aa9554Sclaudio 			if (errno == EPIPE)	/* connection closed */
25242a8b0bcSkrw 				shut = 1;
253c1aa9554Sclaudio 			else
254dd7efffeSclaudio 				fatal("imsgbuf_write");
255c1aa9554Sclaudio 		}
256f6242408Spyr 	}
257f6242408Spyr 
258f6242408Spyr 	for (;;) {
259f6242408Spyr 		if ((n = imsg_get(ibuf, &imsg)) == -1)
260fdca1675Szinovik 			fatal("client_dispatch_parent: imsg_get error");
261f6242408Spyr 		if (n == 0)
262f6242408Spyr 			break;
263f6242408Spyr 
264f6242408Spyr 		switch (imsg.hdr.type) {
265f6242408Spyr 		case IMSG_CONF_START: {
266f6242408Spyr 			struct env	params;
267f6242408Spyr 
268f6242408Spyr 			if (env->sc_flags & F_CONFIGURING) {
269f6242408Spyr 				log_warnx("configuration already in progress");
270f6242408Spyr 				break;
271f6242408Spyr 			}
272f6242408Spyr 			memcpy(&params, imsg.data, sizeof(params));
273f6242408Spyr 			log_debug("configuration starting");
274f6242408Spyr 			env->sc_flags |= F_CONFIGURING;
275f6242408Spyr 			purge_config(env);
276f6242408Spyr 			memcpy(&env->sc_conf_tv, &params.sc_conf_tv,
277f6242408Spyr 			    sizeof(env->sc_conf_tv));
278f6242408Spyr 			env->sc_flags |= params.sc_flags;
279f6242408Spyr 			break;
280f6242408Spyr 		}
281f6242408Spyr 		case IMSG_CONF_IDM: {
282f6242408Spyr 			struct idm	*idm;
283f6242408Spyr 
284f6242408Spyr 			if (!(env->sc_flags & F_CONFIGURING))
285f6242408Spyr 				break;
286f6242408Spyr 			if ((idm = calloc(1, sizeof(*idm))) == NULL)
287f6242408Spyr 				fatal(NULL);
288f6242408Spyr 			memcpy(idm, imsg.data, sizeof(*idm));
289f6242408Spyr 			idm->idm_env = env;
290f6242408Spyr 			TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry);
291f6242408Spyr 			break;
292f6242408Spyr 		}
293f6242408Spyr 		case IMSG_CONF_END:
294f6242408Spyr 			env->sc_flags &= ~F_CONFIGURING;
295f6242408Spyr 			log_debug("applying configuration");
296f6242408Spyr 			client_configure(env);
297f6242408Spyr 			break;
298f6242408Spyr 		default:
299f6242408Spyr 			log_debug("client_dispatch_parent: unexpect imsg %d",
300f6242408Spyr 			    imsg.hdr.type);
301f6242408Spyr 
302f6242408Spyr 			break;
303f6242408Spyr 		}
304f6242408Spyr 		imsg_free(&imsg);
305f6242408Spyr 	}
30642a8b0bcSkrw 
307f6242408Spyr 	if (!shut)
308d46aeb19Seric 		imsg_event_add(iev);
309f6242408Spyr 	else {
310f6242408Spyr 		/* this pipe is dead, so remove the event handler */
311d46aeb19Seric 		event_del(&iev->ev);
312f6242408Spyr 		event_loopexit(NULL);
313f6242408Spyr 	}
314f6242408Spyr }
315f6242408Spyr 
316f6242408Spyr void
317f6242408Spyr client_shutdown(void)
318f6242408Spyr {
319f6242408Spyr 	log_info("ldap client exiting");
320f6242408Spyr 	_exit(0);
321f6242408Spyr }
322f6242408Spyr 
323f6242408Spyr pid_t
324f6242408Spyr ldapclient(int pipe_main2client[2])
325f6242408Spyr {
326509b4fc5Snaddy 	pid_t            pid;
327db474592Saschrijver 	int              pipe_dns[2];
328f6242408Spyr 	struct passwd	*pw;
329f6242408Spyr 	struct event	 ev_sigint;
330f6242408Spyr 	struct event	 ev_sigterm;
33173492e0cSclaudio 	struct event	 ev_sighup;
332f6242408Spyr 	struct env	 env;
333f6242408Spyr 
334f6242408Spyr 	switch (pid = fork()) {
335f6242408Spyr 	case -1:
336f6242408Spyr 		fatal("cannot fork");
337f6242408Spyr 		break;
338f6242408Spyr 	case 0:
339f6242408Spyr 		break;
340f6242408Spyr 	default:
341f6242408Spyr 		return (pid);
342f6242408Spyr 	}
343f6242408Spyr 
34444aaf217Smestre 	memset(&env, 0, sizeof(env));
345f6242408Spyr 	TAILQ_INIT(&env.sc_idms);
346f6242408Spyr 
347f6242408Spyr 	if ((pw = getpwnam(YPLDAP_USER)) == NULL)
348f6242408Spyr 		fatal("getpwnam");
349f6242408Spyr 
350db474592Saschrijver 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dns) == -1)
351db474592Saschrijver 		fatal("socketpair");
352509b4fc5Snaddy 	ypldap_dns(pipe_dns, pw);
353db474592Saschrijver 	close(pipe_dns[1]);
354db474592Saschrijver 
355f6242408Spyr #ifndef DEBUG
356f6242408Spyr 	if (chroot(pw->pw_dir) == -1)
357f6242408Spyr 		fatal("chroot");
358f6242408Spyr 	if (chdir("/") == -1)
359f6242408Spyr 		fatal("chdir");
360f6242408Spyr #else
361f6242408Spyr #warning disabling chrooting in DEBUG mode
362f6242408Spyr #endif
363f6242408Spyr 	setproctitle("ldap client");
3640d2ff2f1Spyr 	ypldap_process = PROC_CLIENT;
3650be9c890Sbenno 	log_procname = log_procnames[ypldap_process];
366f6242408Spyr 
367f6242408Spyr #ifndef DEBUG
368f6242408Spyr 	if (setgroups(1, &pw->pw_gid) ||
369f6242408Spyr 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
370f6242408Spyr 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
371f6242408Spyr 		fatal("cannot drop privileges");
372f6242408Spyr #else
373f6242408Spyr #warning disabling privilege revocation in DEBUG mode
374f6242408Spyr #endif
375f6242408Spyr 
37630135bcbSjmatthew 	if (pledge("stdio inet", NULL) == -1)
37730135bcbSjmatthew 		fatal("pledge");
37830135bcbSjmatthew 
379f6242408Spyr 	event_init();
3801add6c26Sjmatthew 	signal(SIGPIPE, SIG_IGN);
381f6242408Spyr 	signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
382f6242408Spyr 	signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
38373492e0cSclaudio 	signal_set(&ev_sighup, SIGHUP, client_sig_handler, NULL);
384f6242408Spyr 	signal_add(&ev_sigint, NULL);
385f6242408Spyr 	signal_add(&ev_sigterm, NULL);
38673492e0cSclaudio 	signal_add(&ev_sighup, NULL);
387f6242408Spyr 
388f6242408Spyr 	close(pipe_main2client[0]);
389d46aeb19Seric 	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
390f6242408Spyr 		fatal(NULL);
391d46aeb19Seric 	if ((env.sc_iev_dns = calloc(1, sizeof(*env.sc_iev_dns))) == NULL)
392db474592Saschrijver 		fatal(NULL);
393f6242408Spyr 
394d46aeb19Seric 	env.sc_iev->events = EV_READ;
395d46aeb19Seric 	env.sc_iev->data = &env;
396*f1b790a5Sclaudio 	if (imsgbuf_init(&env.sc_iev->ibuf, pipe_main2client[1]) == -1)
397*f1b790a5Sclaudio 		fatal(NULL);
398d46aeb19Seric 	env.sc_iev->handler = client_dispatch_parent;
399d46aeb19Seric 	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
400d46aeb19Seric 	    env.sc_iev->handler, &env);
401d46aeb19Seric 	event_add(&env.sc_iev->ev, NULL);
402f6242408Spyr 
403d46aeb19Seric 	env.sc_iev_dns->events = EV_READ;
404d46aeb19Seric 	env.sc_iev_dns->data = &env;
405*f1b790a5Sclaudio 	if (imsgbuf_init(&env.sc_iev_dns->ibuf, pipe_dns[0]) == -1)
406*f1b790a5Sclaudio 		fatal(NULL);
407d46aeb19Seric 	env.sc_iev_dns->handler = client_dispatch_dns;
408d46aeb19Seric 	event_set(&env.sc_iev_dns->ev, env.sc_iev_dns->ibuf.fd,
409d46aeb19Seric 	    env.sc_iev_dns->events, env.sc_iev_dns->handler, &env);
410d46aeb19Seric 	event_add(&env.sc_iev_dns->ev, NULL);
411db474592Saschrijver 
412f6242408Spyr 	event_dispatch();
413f6242408Spyr 	client_shutdown();
414f6242408Spyr 
415f6242408Spyr 	return (0);
416f6242408Spyr 
417f6242408Spyr }
418f6242408Spyr 
419ec4ad443Saschrijver int
42068e6d159Smartinh client_build_req(struct idm *idm, struct idm_req *ir, struct aldap_message *m,
42168e6d159Smartinh     int min_attr, int max_attr)
42268e6d159Smartinh {
423d464d0deSmartijn 	struct aldap_stringset	*ldap_attrs;
424d464d0deSmartijn 	int	 i;
425d464d0deSmartijn 	size_t	 k;
42668e6d159Smartinh 
42744aaf217Smestre 	memset(ir, 0, sizeof(*ir));
42868e6d159Smartinh 	for (i = min_attr; i < max_attr; i++) {
42968e6d159Smartinh 		if (idm->idm_flags & F_FIXED_ATTR(i)) {
43068e6d159Smartinh 			if (strlcat(ir->ir_line, idm->idm_attrs[i],
43168e6d159Smartinh 			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
43268e6d159Smartinh 				/*
43368e6d159Smartinh 				 * entry yields a line > 1024, trash it.
43468e6d159Smartinh 				 */
43568e6d159Smartinh 				return (-1);
43668e6d159Smartinh 
43768e6d159Smartinh 			if (i == ATTR_UID) {
43868e6d159Smartinh 				ir->ir_key.ik_uid = strtonum(
43968e6d159Smartinh 				    idm->idm_attrs[i], 0,
44068e6d159Smartinh 				    UID_MAX, NULL);
44168e6d159Smartinh 			} else if (i == ATTR_GR_GID) {
44268e6d159Smartinh 				ir->ir_key.ik_gid = strtonum(
44368e6d159Smartinh 				    idm->idm_attrs[i], 0,
44468e6d159Smartinh 				    GID_MAX, NULL);
44568e6d159Smartinh 			}
44668e6d159Smartinh 		} else if (idm->idm_list & F_LIST(i)) {
4470099bae5Sjmatthew 			aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs);
448d464d0deSmartijn 			for (k = 0; k >= 0 && ldap_attrs && k < ldap_attrs->len; k++) {
4490099bae5Sjmatthew 				/* XXX: Fail when attributes have illegal characters e.g. ',' */
450d464d0deSmartijn 				if (strlcat(ir->ir_line,
451d464d0deSmartijn 				    ldap_attrs->str[k].ostr_val,
45268e6d159Smartinh 				    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
45368e6d159Smartinh 					continue;
454d464d0deSmartijn 				if (k + 1 < ldap_attrs->len)
45568e6d159Smartinh 					if (strlcat(ir->ir_line, ",",
45668e6d159Smartinh 						    sizeof(ir->ir_line))
45768e6d159Smartinh 					    >= sizeof(ir->ir_line)) {
458e971cb85Saschrijver 						aldap_free_attr(ldap_attrs);
45968e6d159Smartinh 						return (-1);
46068e6d159Smartinh 					}
46168e6d159Smartinh 			}
462e971cb85Saschrijver 			aldap_free_attr(ldap_attrs);
46368e6d159Smartinh 		} else {
464e971cb85Saschrijver 			if (aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs) == -1)
46568e6d159Smartinh 				return (-1);
466d464d0deSmartijn 			if (strlcat(ir->ir_line, ldap_attrs->str[0].ostr_val,
46768e6d159Smartinh 			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) {
468e971cb85Saschrijver 				aldap_free_attr(ldap_attrs);
46968e6d159Smartinh 				return (-1);
47068e6d159Smartinh 			}
47168e6d159Smartinh 			if (i == ATTR_UID) {
47268e6d159Smartinh 				ir->ir_key.ik_uid = strtonum(
473d464d0deSmartijn 				    ldap_attrs->str[0].ostr_val, 0, UID_MAX,
474d464d0deSmartijn 				    NULL);
47568e6d159Smartinh 			} else if (i == ATTR_GR_GID) {
47668e6d159Smartinh 				ir->ir_key.ik_uid = strtonum(
477d464d0deSmartijn 				    ldap_attrs->str[0].ostr_val, 0, GID_MAX,
478d464d0deSmartijn 				    NULL);
47968e6d159Smartinh 			}
480e971cb85Saschrijver 			aldap_free_attr(ldap_attrs);
48168e6d159Smartinh 		}
48268e6d159Smartinh 
48368e6d159Smartinh 		if (i + 1 != max_attr)
48468e6d159Smartinh 			if (strlcat(ir->ir_line, ":",
48568e6d159Smartinh 			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
48668e6d159Smartinh 				return (-1);
48768e6d159Smartinh 	}
48868e6d159Smartinh 
48968e6d159Smartinh 	return (0);
49068e6d159Smartinh }
49168e6d159Smartinh 
49268e6d159Smartinh int
493bb704a6bSmartinh client_search_idm(struct env *env, struct idm *idm, struct aldap *al,
494bb704a6bSmartinh     char **attrs, char *filter, int min_attr, int max_attr,
495bb704a6bSmartinh     enum imsg_type type)
496bb704a6bSmartinh {
497bb704a6bSmartinh 	struct idm_req		 ir;
498bb704a6bSmartinh 	struct aldap_message	*m;
499c89bbd07Sjmatthew 	struct aldap_page_control *pg = NULL;
500bb704a6bSmartinh 	const char		*errstr;
501b8ccc478Sjmatthew 	char			*dn;
502bb704a6bSmartinh 
503b8ccc478Sjmatthew 	dn = idm->idm_basedn;
504b8ccc478Sjmatthew 	if (type == IMSG_GRP_ENTRY && idm->idm_groupdn[0] != '\0')
505b8ccc478Sjmatthew 		dn = idm->idm_groupdn;
506b8ccc478Sjmatthew 
507c89bbd07Sjmatthew 	do {
508b8ccc478Sjmatthew 		if (aldap_search(al, dn, LDAP_SCOPE_SUBTREE,
509c89bbd07Sjmatthew 		    filter, attrs, 0, 0, 0, pg) == -1) {
510bb704a6bSmartinh 			aldap_get_errno(al, &errstr);
511bb704a6bSmartinh 			log_debug("%s", errstr);
512bb704a6bSmartinh 			return (-1);
513bb704a6bSmartinh 		}
514bb704a6bSmartinh 
515c89bbd07Sjmatthew 		if (pg != NULL) {
516c89bbd07Sjmatthew 			aldap_freepage(pg);
517c89bbd07Sjmatthew 			pg = NULL;
518c89bbd07Sjmatthew 		}
519c89bbd07Sjmatthew 
520bb704a6bSmartinh 		while ((m = aldap_parse(al)) != NULL) {
521bb704a6bSmartinh 			if (al->msgid != m->msgid) {
522c89bbd07Sjmatthew 				goto fail;
523bb704a6bSmartinh 			}
524c89bbd07Sjmatthew 
525bb704a6bSmartinh 			if (m->message_type == LDAP_RES_SEARCH_RESULT) {
526c89bbd07Sjmatthew 				if (m->page != NULL && m->page->cookie_len != 0)
527c89bbd07Sjmatthew 					pg = m->page;
528c89bbd07Sjmatthew 				else
529c89bbd07Sjmatthew 					pg = NULL;
530c89bbd07Sjmatthew 
531bb704a6bSmartinh 				aldap_freemsg(m);
532bb704a6bSmartinh 				break;
533bb704a6bSmartinh 			}
534c89bbd07Sjmatthew 
535bb704a6bSmartinh 			if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
536c89bbd07Sjmatthew 				goto fail;
537bb704a6bSmartinh 			}
538bb704a6bSmartinh 
539bb704a6bSmartinh 			if (client_build_req(idm, &ir, m, min_attr, max_attr) == 0)
540bb704a6bSmartinh 				imsg_compose_event(env->sc_iev, type, 0, 0, -1,
54145c961faSjmatthew 				    &ir, sizeof(ir.ir_key) +
54245c961faSjmatthew 				    strlen(ir.ir_line) + 1);
543c89bbd07Sjmatthew 
544bb704a6bSmartinh 			aldap_freemsg(m);
545bb704a6bSmartinh 		}
546c89bbd07Sjmatthew 	} while (pg != NULL);
547bb704a6bSmartinh 
548bb704a6bSmartinh 	return (0);
549c89bbd07Sjmatthew 
550c89bbd07Sjmatthew fail:
551c89bbd07Sjmatthew 	aldap_freemsg(m);
552c89bbd07Sjmatthew 	if (pg != NULL) {
553c89bbd07Sjmatthew 		aldap_freepage(pg);
554c89bbd07Sjmatthew 	}
555c89bbd07Sjmatthew 
556c89bbd07Sjmatthew 	return (-1);
557bb704a6bSmartinh }
558bb704a6bSmartinh 
559bb704a6bSmartinh int
56034d9787bSjmatthew client_try_idm(struct env *env, struct idm *idm, struct ypldap_addr *addr)
561ec4ad443Saschrijver {
562bb704a6bSmartinh 	const char		*where;
56334d9787bSjmatthew 	char			 hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
564db474592Saschrijver 	char			*attrs[ATTR_MAX+1];
56534d9787bSjmatthew 	int			 fd = -1;
56668e6d159Smartinh 	int			 i, j;
56734d9787bSjmatthew 	struct sockaddr		*sa = (struct sockaddr *)&addr->ss;
568ec4ad443Saschrijver 	struct aldap_message	*m;
569ec4ad443Saschrijver 	struct aldap		*al;
570ec4ad443Saschrijver 
57134d9787bSjmatthew 	if (getnameinfo(sa, SA_LEN(sa), hbuf, sizeof(hbuf), sbuf,
57234d9787bSjmatthew 	    sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV))
57334d9787bSjmatthew 		errx(1, "could not get numeric hostname");
57434d9787bSjmatthew 
575ec4ad443Saschrijver 	where = "connect";
57634d9787bSjmatthew 	if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1)
577db474592Saschrijver 		return (-1);
578ec4ad443Saschrijver 
57934d9787bSjmatthew 	if (connect(fd, sa, SA_LEN(sa)) != 0) {
58034d9787bSjmatthew 		log_warn("connect to %s port %s failed", hbuf, sbuf);
58134d9787bSjmatthew 		close(fd);
58234d9787bSjmatthew 		return (-1);
58334d9787bSjmatthew 	}
58434d9787bSjmatthew 
58534d9787bSjmatthew 	al = aldap_init(fd);
58634d9787bSjmatthew 	if (al == NULL) {
58734d9787bSjmatthew 		close(fd);
58834d9787bSjmatthew 		return (-1);
58934d9787bSjmatthew 	}
59034d9787bSjmatthew 
59132814761Sjmatthew 	if (idm->idm_flags & F_STARTTLS) {
59232814761Sjmatthew 		log_debug("requesting starttls");
59332814761Sjmatthew 		where = "starttls";
59432814761Sjmatthew 		if (aldap_req_starttls(al) == -1)
59532814761Sjmatthew 			goto bad;
59632814761Sjmatthew 
59732814761Sjmatthew 		where = "parsing";
59832814761Sjmatthew 		if ((m = aldap_parse(al)) == NULL)
59932814761Sjmatthew 			goto bad;
60032814761Sjmatthew 		where = "verifying msgid";
60132814761Sjmatthew 		if (al->msgid != m->msgid) {
60232814761Sjmatthew 			aldap_freemsg(m);
60332814761Sjmatthew 			goto bad;
60432814761Sjmatthew 		}
60532814761Sjmatthew 		where = "starttls result";
60632814761Sjmatthew 		if (aldap_get_resultcode(m) != LDAP_SUCCESS) {
60732814761Sjmatthew 			aldap_freemsg(m);
60832814761Sjmatthew 			goto bad;
60932814761Sjmatthew 		}
61032814761Sjmatthew 		aldap_freemsg(m);
61132814761Sjmatthew 	}
61232814761Sjmatthew 
61332814761Sjmatthew 	if (idm->idm_flags & (F_STARTTLS | F_SSL)) {
61432814761Sjmatthew 		log_debug("starting tls");
61532814761Sjmatthew 		where = "enabling tls";
61632814761Sjmatthew 		if (aldap_tls(al, idm->idm_tls_config, idm->idm_name) < 0) {
61732814761Sjmatthew 			const char *err;
61832814761Sjmatthew 			aldap_get_errno(al, &err);
61934d9787bSjmatthew 			log_warnx("TLS handshake with %s(%s) failed: %s",
62034d9787bSjmatthew 			    idm->idm_name, hbuf, err);
62132814761Sjmatthew 			goto bad;
62232814761Sjmatthew 		}
62332814761Sjmatthew 	}
62432814761Sjmatthew 
625ec4ad443Saschrijver 	if (idm->idm_flags & F_NEEDAUTH) {
626b245f644Sjmatthew 		int rc;
627b245f644Sjmatthew 
628ec4ad443Saschrijver 		where = "binding";
62940c94266Sjmatthew 		if (idm->idm_bindext != 0)
63040c94266Sjmatthew 			rc = aldap_bind_sasl_external(al, idm->idm_bindextid);
63140c94266Sjmatthew 		else
63240c94266Sjmatthew 			rc = aldap_bind(al, idm->idm_binddn, idm->idm_bindcred);
63340c94266Sjmatthew 		if (rc == -1)
634ec4ad443Saschrijver 			goto bad;
635ec4ad443Saschrijver 
636ec4ad443Saschrijver 		where = "parsing";
637ec4ad443Saschrijver 		if ((m = aldap_parse(al)) == NULL)
638ec4ad443Saschrijver 			goto bad;
639ec4ad443Saschrijver 		where = "verifying msgid";
640ec4ad443Saschrijver 		if (al->msgid != m->msgid) {
641ec4ad443Saschrijver 			aldap_freemsg(m);
642ec4ad443Saschrijver 			goto bad;
643ec4ad443Saschrijver 		}
644b245f644Sjmatthew 		where = "bind response";
645b245f644Sjmatthew 		rc = aldap_get_resultcode(m);
646b245f644Sjmatthew 		if (rc != LDAP_SUCCESS) {
64734d9787bSjmatthew 			log_warnx("LDAP bind with %s(%s) failed: result code"
64834d9787bSjmatthew 			    " %d", idm->idm_name, hbuf, rc);
649b245f644Sjmatthew 			aldap_freemsg(m);
650b245f644Sjmatthew 			goto bad;
651b245f644Sjmatthew 		}
652ec4ad443Saschrijver 		aldap_freemsg(m);
653ec4ad443Saschrijver 	}
654ec4ad443Saschrijver 
65544aaf217Smestre 	memset(attrs, 0, sizeof(attrs));
656db474592Saschrijver 	for (i = 0, j = 0; i < ATTR_MAX; i++) {
657db474592Saschrijver 		if (idm->idm_flags & F_FIXED_ATTR(i))
658db474592Saschrijver 			continue;
659db474592Saschrijver 		attrs[j++] = idm->idm_attrs[i];
660db474592Saschrijver 	}
661db474592Saschrijver 	attrs[j] = NULL;
662db474592Saschrijver 
663db474592Saschrijver 	/*
664db474592Saschrijver 	 * build password line.
665db474592Saschrijver 	 */
666bb704a6bSmartinh 	where = "search";
667bb704a6bSmartinh 	log_debug("searching password entries");
668bb704a6bSmartinh 	if (client_search_idm(env, idm, al, attrs,
669bb704a6bSmartinh 	    idm->idm_filters[FILTER_USER], 0, ATTR_MAX, IMSG_PW_ENTRY) == -1)
670db474592Saschrijver 		goto bad;
671db474592Saschrijver 
67244aaf217Smestre 	memset(attrs, 0, sizeof(attrs));
673db474592Saschrijver 	for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) {
674db474592Saschrijver 		if (idm->idm_flags & F_FIXED_ATTR(i))
675db474592Saschrijver 			continue;
676db474592Saschrijver 		attrs[j++] = idm->idm_attrs[i];
677db474592Saschrijver 	}
678db474592Saschrijver 	attrs[j] = NULL;
679db474592Saschrijver 
680db474592Saschrijver 	/*
681db474592Saschrijver 	 * build group line.
682db474592Saschrijver 	 */
683bb704a6bSmartinh 	where = "search";
684bb704a6bSmartinh 	log_debug("searching group entries");
685bb704a6bSmartinh 	if (client_search_idm(env, idm, al, attrs,
686bb704a6bSmartinh 	    idm->idm_filters[FILTER_GROUP], ATTR_GR_MIN, ATTR_GR_MAX,
687bb704a6bSmartinh 	    IMSG_GRP_ENTRY) == -1)
688db474592Saschrijver 		goto bad;
689ec4ad443Saschrijver 
690ec4ad443Saschrijver 	aldap_close(al);
691ec4ad443Saschrijver 
692db474592Saschrijver 	idm->idm_state = STATE_LDAP_DONE;
693db474592Saschrijver 
694ec4ad443Saschrijver 	return (0);
695ec4ad443Saschrijver bad:
6965ea048ddSzinovik 	aldap_close(al);
69734d9787bSjmatthew 	log_debug("directory %s(%s) errored out in %s", idm->idm_name, hbuf,
69834d9787bSjmatthew 	    where);
699ec4ad443Saschrijver 	return (-1);
700ec4ad443Saschrijver }
701ec4ad443Saschrijver 
702f6242408Spyr void
703db474592Saschrijver client_periodic_update(int fd, short event, void *p)
704db474592Saschrijver {
705db474592Saschrijver 	struct env	*env = p;
706db474592Saschrijver 
707db474592Saschrijver 	struct idm	*idm;
708db474592Saschrijver 	int		 fail_cnt = 0;
709db474592Saschrijver 
710db474592Saschrijver 	/* If LDAP isn't finished, notify the master process to trash the
711db474592Saschrijver 	 * update. */
712db474592Saschrijver 	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
713db474592Saschrijver 		if (idm->idm_state < STATE_LDAP_DONE)
714db474592Saschrijver 			fail_cnt++;
715db474592Saschrijver 
716db474592Saschrijver 		idm->idm_state = STATE_NONE;
717db474592Saschrijver 
718db474592Saschrijver 		client_addr_free(idm);
719db474592Saschrijver 	}
720db474592Saschrijver 	if (fail_cnt > 0) {
721db474592Saschrijver 		log_debug("trash the update");
722d46aeb19Seric 		imsg_compose_event(env->sc_iev, IMSG_TRASH_UPDATE, 0, 0, -1,
723d46aeb19Seric 		    NULL, 0);
724db474592Saschrijver 	}
725db474592Saschrijver 
726db474592Saschrijver 	client_configure(env);
727db474592Saschrijver }
728db474592Saschrijver 
729db474592Saschrijver void
730f6242408Spyr client_configure(struct env *env)
731f6242408Spyr {
732f6242408Spyr 	struct timeval	 tv;
733f6242408Spyr 	struct idm	*idm;
734db474592Saschrijver         u_int16_t        dlen;
735f6242408Spyr 
736f6242408Spyr 	log_debug("connecting to directories");
737db474592Saschrijver 
738d46aeb19Seric 	imsg_compose_event(env->sc_iev, IMSG_START_UPDATE, 0, 0, -1, NULL, 0);
739db474592Saschrijver 
740db474592Saschrijver 	/* Start the DNS lookups */
741db474592Saschrijver 	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
742db474592Saschrijver 		dlen = strlen(idm->idm_name) + 1;
743d46aeb19Seric 		imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id,
744d46aeb19Seric 		    0, -1, idm->idm_name, dlen);
745f6242408Spyr 	}
746db474592Saschrijver 
747f6242408Spyr 	tv.tv_sec = env->sc_conf_tv.tv_sec;
748f6242408Spyr 	tv.tv_usec = env->sc_conf_tv.tv_usec;
749db474592Saschrijver 	evtimer_set(&env->sc_conf_ev, client_periodic_update, env);
750f6242408Spyr 	evtimer_add(&env->sc_conf_ev, &tv);
751f6242408Spyr }
752