xref: /openbsd/usr.sbin/ldapd/ldape.c (revision 135f245e)
1 /*	$OpenBSD: ldape.c,v 1.38 2024/01/17 08:28:15 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/queue.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <sys/un.h>
24 #include <sys/wait.h>
25 
26 #include <err.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "ldapd.h"
34 #include "log.h"
35 
36 void			 ldape_sig_handler(int fd, short why, void *data);
37 static void		 ldape_auth_result(struct imsg *imsg);
38 static void		 ldape_open_result(struct imsg *imsg);
39 static void		 ldape_imsgev(struct imsgev *iev, int code,
40 			    struct imsg *imsg);
41 static void		 ldape_needfd(struct imsgev *iev);
42 
43 int			 ldap_starttls(struct request *req);
44 void			 send_ldap_extended_response(struct conn *conn,
45 				int msgid, unsigned int type,
46 				long long result_code,
47 				const char *extended_oid);
48 
49 struct imsgev		*iev_ldapd;
50 struct control_sock	 csock;
51 
52 void
ldape_sig_handler(int sig,short why,void * data)53 ldape_sig_handler(int sig, short why, void *data)
54 {
55 	log_debug("ldape: got signal %d", sig);
56 	if (sig == SIGCHLD) {
57 		for (;;) {
58 			pid_t	 pid;
59 			int	 status;
60 
61 			pid = waitpid(WAIT_ANY, &status, WNOHANG);
62 			if (pid <= 0)
63 				break;
64 		}
65 		return;
66 	}
67 
68 	event_loopexit(NULL);
69 }
70 
71 void
send_ldap_extended_response(struct conn * conn,int msgid,unsigned int type,long long result_code,const char * extended_oid)72 send_ldap_extended_response(struct conn *conn, int msgid, unsigned int type,
73     long long result_code, const char *extended_oid)
74 {
75 	ssize_t			 rc;
76 	struct ber_element	*root, *elm;
77 	void			*buf;
78 
79 	log_debug("sending response %u with result %lld", type, result_code);
80 
81 	if ((root = ober_add_sequence(NULL)) == NULL)
82 		goto fail;
83 
84 	elm = ober_printf_elements(root, "d{tEss",
85 	    msgid, BER_CLASS_APP, type, result_code, "", "");
86 	if (elm == NULL)
87 		goto fail;
88 
89 	if (extended_oid)
90 		if (ober_add_string(elm, extended_oid) == NULL)
91 			goto fail;
92 
93 	ldap_debug_elements(root, type, "sending response on fd %d", conn->fd);
94 
95 	rc = ober_write_elements(&conn->ber, root);
96 	ober_free_elements(root);
97 
98 	if (rc < 0)
99 		log_warn("failed to create ldap result");
100 	else {
101 		ober_get_writebuf(&conn->ber, &buf);
102 		if (bufferevent_write(conn->bev, buf, rc) != 0)
103 			log_warn("failed to send ldap result");
104 	}
105 
106 	return;
107 fail:
108 	if (root)
109 		ober_free_elements(root);
110 }
111 
112 int
ldap_refer(struct request * req,const char * basedn,struct search * search,struct referrals * refs)113 ldap_refer(struct request *req, const char *basedn, struct search *search,
114     struct referrals *refs)
115 {
116 	struct ber_element	*root, *elm, *ref_root = NULL;
117 	struct referral		*ref;
118 	long long		 result_code = LDAP_REFERRAL;
119 	unsigned int		 type;
120 	ssize_t			 rc;
121 	void			*buf;
122 	char			*url, *scope_str = NULL;
123 
124 	if (req->type == LDAP_REQ_SEARCH)
125 		type = LDAP_RES_SEARCH_RESULT;
126 	else
127 		type = req->type + 1;
128 
129 	if (search != NULL) {
130 		if (search->scope != LDAP_SCOPE_SUBTREE)
131 			scope_str = "base";
132 		else
133 			scope_str = "sub";
134 	}
135 
136 	log_debug("sending referral in response %u on msgid %lld",
137 	    type, req->msgid);
138 
139 	if ((root = ober_add_sequence(NULL)) == NULL)
140 		goto fail;
141 
142 	if ((elm = ref_root = ober_add_sequence(NULL)) == NULL)
143 		goto fail;
144 	ober_set_header(ref_root, BER_CLASS_CONTEXT, LDAP_REQ_SEARCH);
145 	SLIST_FOREACH(ref, refs, next) {
146 		if (search != NULL)
147 			rc = asprintf(&url, "%s/%s??%s", ref->url, basedn,
148 			    scope_str);
149 		else
150 			rc = asprintf(&url, "%s/%s", ref->url, basedn);
151 		if (rc == -1) {
152 			log_warn("asprintf");
153 			goto fail;
154 		}
155 		log_debug("adding referral '%s'", url);
156 		elm = ober_add_string(elm, url);
157 		free(url);
158 		if (elm == NULL)
159 			goto fail;
160 	}
161 
162 	elm = ober_printf_elements(root, "d{tEsse",
163 	    req->msgid, BER_CLASS_APP, type, result_code, "", "", ref_root);
164 	if (elm == NULL)
165 		goto fail;
166 	ref_root = NULL;
167 
168 	rc = ober_write_elements(&req->conn->ber, root);
169 	ober_free_elements(root);
170 
171 	if (rc < 0)
172 		log_warn("failed to create ldap result");
173 	else {
174 		ober_get_writebuf(&req->conn->ber, &buf);
175 		if (bufferevent_write(req->conn->bev, buf, rc) != 0)
176 			log_warn("failed to send ldap result");
177 	}
178 
179 	request_free(req);
180 	return LDAP_REFERRAL;
181 
182 fail:
183 	if (root != NULL)
184 		ober_free_elements(root);
185 	if (ref_root != NULL)
186 		ober_free_elements(ref_root);
187 	request_free(req);
188 	return LDAP_REFERRAL;
189 }
190 
191 void
send_ldap_result(struct conn * conn,int msgid,unsigned int type,long long result_code)192 send_ldap_result(struct conn *conn, int msgid, unsigned int type,
193     long long result_code)
194 {
195 	send_ldap_extended_response(conn, msgid, type, result_code, NULL);
196 }
197 
198 int
ldap_respond(struct request * req,int code)199 ldap_respond(struct request *req, int code)
200 {
201 	if (code >= 0)
202 		send_ldap_result(req->conn, req->msgid, req->type + 1, code);
203 	request_free(req);
204 	return code;
205 }
206 
207 int
ldap_abandon(struct request * req)208 ldap_abandon(struct request *req)
209 {
210 	long long	 msgid;
211 	struct search	*search;
212 
213 	if (ober_scanf_elements(req->op, "i", &msgid) != 0) {
214 		request_free(req);
215 		return -1;	/* protocol error, but don't respond */
216 	}
217 
218 	TAILQ_FOREACH(search, &req->conn->searches, next) {
219 		if (search->req->msgid == msgid) {
220 			/* unlinks the search from conn->searches */
221 			search_close(search);
222 			break;
223 		}
224 	}
225 	request_free(req);
226 	return -1;
227 }
228 
229 int
ldap_unbind(struct request * req)230 ldap_unbind(struct request *req)
231 {
232 	log_debug("current bind dn = %s",
233 	    req->conn->binddn == NULL ? "" : req->conn->binddn);
234 	conn_disconnect(req->conn);
235 	request_free(req);
236 	return -1;		/* don't send any response */
237 }
238 
239 int
ldap_compare(struct request * req)240 ldap_compare(struct request *req)
241 {
242 	struct ber_element	*entry, *elm, *attr;
243 	struct namespace	*ns;
244 	struct referrals	*refs;
245 	struct attr_type	*at;
246 	char			*dn, *aname, *value, *s;
247 
248 	if (ober_scanf_elements(req->op, "{s{ss", &dn, &aname, &value) != 0) {
249 		log_debug("%s: protocol error", __func__);
250 		request_free(req);
251 		return -1;
252 	}
253 
254 	if ((at = lookup_attribute(conf->schema, aname)) == NULL)
255 		return ldap_respond(req, LDAP_UNDEFINED_TYPE);
256 
257 	if ((ns = namespace_for_base(dn)) == NULL) {
258 		refs = namespace_referrals(dn);
259 		if (refs == NULL)
260 			return ldap_respond(req, LDAP_NO_SUCH_OBJECT);
261 		else
262 			return ldap_refer(req, dn, NULL, refs);
263 	}
264 
265 	if ((entry = namespace_get(ns, dn)) == NULL)
266 		return ldap_respond(req, LDAP_NO_SUCH_OBJECT);
267 
268 	if ((attr = ldap_find_attribute(entry, at)) == NULL)
269 		return ldap_respond(req, LDAP_NO_SUCH_ATTRIBUTE);
270 
271 	if ((attr = attr->be_next) == NULL)	/* skip attribute name */
272 		return ldap_respond(req, LDAP_OTHER);
273 
274 	for (elm = attr->be_sub; elm != NULL; elm = elm->be_next) {
275 		if (ober_get_string(elm, &s) != 0)
276 			return ldap_respond(req, LDAP_OTHER);
277 		if (strcasecmp(value, s) == 0)
278 			return ldap_respond(req, LDAP_COMPARE_TRUE);
279 	}
280 
281 	return ldap_respond(req, LDAP_COMPARE_FALSE);
282 }
283 
284 int
ldap_starttls(struct request * req)285 ldap_starttls(struct request *req)
286 {
287 	if ((req->conn->listener->flags & F_STARTTLS) == 0) {
288 		log_debug("StartTLS not configured for this connection");
289 		return LDAP_OPERATIONS_ERROR;
290 	}
291 
292 	req->conn->s_flags |= F_STARTTLS;
293 	return LDAP_SUCCESS;
294 }
295 
296 int
ldap_extended(struct request * req)297 ldap_extended(struct request *req)
298 {
299 	int			 i, rc = LDAP_PROTOCOL_ERROR;
300 	char			*oid = NULL;
301 	struct {
302 		const char	*oid;
303 		int (*fn)(struct request *);
304 	} extended_ops[] = {
305 		{ "1.3.6.1.4.1.1466.20037", ldap_starttls },
306 		{ NULL }
307 	};
308 
309 	if (ober_scanf_elements(req->op, "{s", &oid) != 0)
310 		goto done;
311 
312 	log_debug("got extended operation %s", oid);
313 	req->op = req->op->be_sub->be_next;
314 
315 	for (i = 0; extended_ops[i].oid != NULL; i++) {
316 		if (strcmp(oid, extended_ops[i].oid) == 0) {
317 			rc = extended_ops[i].fn(req);
318 			break;
319 		}
320 	}
321 
322 	if (extended_ops[i].fn == NULL)
323 		log_warnx("unimplemented extended operation %s", oid);
324 
325 done:
326 	send_ldap_extended_response(req->conn, req->msgid, LDAP_RES_EXTENDED,
327 	    rc, oid);
328 
329 	request_free(req);
330 	return 0;
331 }
332 
333 void
ldape(int debug,int verbose,char * csockpath)334 ldape(int debug, int verbose, char *csockpath)
335 {
336 	int			 on = 1;
337 	struct namespace	*ns;
338 	struct listener		*l;
339 	struct sockaddr_un	*sun = NULL;
340 	struct event		 ev_sigint;
341 	struct event		 ev_sigterm;
342 	struct event		 ev_sigchld;
343 	struct event		 ev_sighup;
344 	struct ssl		 key;
345 	struct passwd		*pw;
346 	char			 host[128];
347 	mode_t			old_umask = 0;
348 
349 	TAILQ_INIT(&conn_list);
350 
351 	ldap_loginit("ldap server", debug, verbose);
352 	setproctitle("ldap server");
353 	event_init();
354 
355 	signal_set(&ev_sigint, SIGINT, ldape_sig_handler, NULL);
356 	signal_set(&ev_sigterm, SIGTERM, ldape_sig_handler, NULL);
357 	signal_set(&ev_sigchld, SIGCHLD, ldape_sig_handler, NULL);
358 	signal_set(&ev_sighup, SIGHUP, ldape_sig_handler, NULL);
359 	signal_add(&ev_sigint, NULL);
360 	signal_add(&ev_sigterm, NULL);
361 	signal_add(&ev_sigchld, NULL);
362 	signal_add(&ev_sighup, NULL);
363 	signal(SIGPIPE, SIG_IGN);
364 
365 	/* Initialize parent imsg events. */
366 	if ((iev_ldapd = calloc(1, sizeof(struct imsgev))) == NULL)
367 		fatal("calloc");
368 	imsgev_init(iev_ldapd, PROC_PARENT_SOCK_FILENO, NULL, ldape_imsgev,
369 	    ldape_needfd);
370 
371 	/* Initialize control socket. */
372 	memset(&csock, 0, sizeof(csock));
373 	csock.cs_name = csockpath;
374 	control_init(&csock);
375 	control_listen(&csock);
376 
377 	/* Initialize LDAP listeners.
378 	 */
379 	TAILQ_FOREACH(l, &conf->listeners, entry) {
380 		l->fd = socket(l->ss.ss_family, SOCK_STREAM | SOCK_NONBLOCK,
381 		    0);
382 		if (l->fd == -1)
383 			fatal("ldape: socket");
384 
385 		setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
386 
387 		if (l->ss.ss_family == AF_UNIX) {
388 			sun = (struct sockaddr_un *)&l->ss;
389 			log_info("listening on %s", sun->sun_path);
390 			if (unlink(sun->sun_path) == -1 && errno != ENOENT)
391 				fatal("ldape: unlink");
392 		} else {
393 			print_host(&l->ss, host, sizeof(host));
394 			log_info("listening on %s:%d", host, ntohs(l->port));
395 		}
396 
397 		if (l->ss.ss_family == AF_UNIX) {
398 			old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
399 		}
400 
401 		if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) != 0)
402 			fatal("ldape: bind");
403 
404 		if (l->ss.ss_family == AF_UNIX) {
405 			mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
406 
407 			(void)umask(old_umask);
408 			if (chmod(sun->sun_path, mode) == -1) {
409 				unlink(sun->sun_path);
410 				fatal("ldape: chmod");
411 			}
412 		}
413 
414 		if (listen(l->fd, 20) != 0)
415 			fatal("ldape: listen");
416 
417 		event_set(&l->ev, l->fd, EV_READ, conn_accept, l);
418 		event_add(&l->ev, NULL);
419 		evtimer_set(&l->evt, conn_accept, l);
420 
421 		if (l->flags & F_SSL) {
422 			if (strlcpy(key.ssl_name, l->ssl_cert_name,
423 			    sizeof(key.ssl_name)) >= sizeof(key.ssl_name))
424 				fatal("ldape: certificate name truncated");
425 
426 			l->ssl = SPLAY_FIND(ssltree, conf->sc_ssl, &key);
427 			if (l->ssl == NULL)
428 				fatal("ldape: certificate tree corrupted");
429 
430 			l->tls = tls_server();
431 			if (l->tls == NULL)
432 				fatal("ldape: couldn't allocate tls context");
433 
434 			if (tls_configure(l->tls, l->ssl->config)) {
435 				log_warnx("ldape: %s", tls_error(l->tls));
436 				fatalx("ldape: couldn't configure tls");
437 			}
438 		}
439 	}
440 
441 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
442 		if (!namespace_has_referrals(ns) && namespace_open(ns) != 0)
443 			fatal("%s", ns->suffix);
444 	}
445 
446 	if ((pw = getpwnam(LDAPD_USER)) == NULL)
447 		fatal("getpwnam");
448 
449 	if (pw != NULL) {
450 		if (chroot(pw->pw_dir) == -1)
451 			fatal("chroot");
452 		if (chdir("/") == -1)
453 			fatal("chdir(\"/\")");
454 
455 		if (setgroups(1, &pw->pw_gid) ||
456 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
457 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
458 			fatal("cannot drop privileges");
459 	}
460 
461 	if (pledge("stdio flock inet unix recvfd", NULL) == -1)
462 		fatal("pledge");
463 
464 	log_debug("ldape: entering event loop");
465 	event_dispatch();
466 
467 	while ((ns = TAILQ_FIRST(&conf->namespaces)) != NULL)
468 		namespace_remove(ns);
469 
470 	control_cleanup(&csock);
471 
472 	log_info("ldape: exiting");
473 	exit(0);
474 }
475 
476 static void
ldape_imsgev(struct imsgev * iev,int code,struct imsg * imsg)477 ldape_imsgev(struct imsgev *iev, int code, struct imsg *imsg)
478 {
479 	switch (code) {
480 	case IMSGEV_IMSG:
481 		log_debug("%s: got imsg %d on fd %d",
482 		    __func__, imsg->hdr.type, iev->ibuf.fd);
483 		switch (imsg->hdr.type) {
484 		case IMSG_LDAPD_AUTH_RESULT:
485 			ldape_auth_result(imsg);
486 			break;
487 		case IMSG_LDAPD_OPEN_RESULT:
488 			ldape_open_result(imsg);
489 			break;
490 		default:
491 			log_debug("%s: unexpected imsg %d",
492 			    __func__, imsg->hdr.type);
493 			break;
494 		}
495 		break;
496 	case IMSGEV_EREAD:
497 	case IMSGEV_EWRITE:
498 	case IMSGEV_EIMSG:
499 		fatal("imsgev read/write error");
500 		break;
501 	case IMSGEV_DONE:
502 		event_loopexit(NULL);
503 		break;
504 	}
505 }
506 
507 static void
ldape_needfd(struct imsgev * iev)508 ldape_needfd(struct imsgev *iev)
509 {
510 	/* Try to close a control connection first */
511 	if (control_close_any(&csock) == 0) {
512 		log_warn("closed a control connection");
513 		return;
514 	}
515 
516 	if (conn_close_any() == 0) {
517 		log_warn("closed a client connection");
518 		return;
519 	}
520 
521 	fatal("unable to free an fd");
522 }
523 
524 static void
ldape_auth_result(struct imsg * imsg)525 ldape_auth_result(struct imsg *imsg)
526 {
527 	struct conn		*conn;
528 	struct auth_res		*ares = imsg->data;
529 
530 	log_debug("authentication on conn %d/%lld = %d", ares->fd, ares->msgid,
531 	    ares->ok);
532 	conn = conn_by_fd(ares->fd);
533 	if (conn->bind_req != NULL && conn->bind_req->msgid == ares->msgid)
534 		ldap_bind_continue(conn, ares->ok);
535 	else
536 		log_warnx("spurious auth result");
537 }
538 
539 static void
ldape_open_result(struct imsg * imsg)540 ldape_open_result(struct imsg *imsg)
541 {
542 	struct namespace	*ns;
543 	struct open_req		*oreq = imsg->data;
544 	int			 fd;
545 
546 	if (imsg->hdr.len != sizeof(*oreq) + IMSG_HEADER_SIZE)
547 		fatal("invalid size of open result");
548 
549 	if (oreq->path[PATH_MAX-1] != '\0')
550 		fatal("bogus path");
551 
552 	fd = imsg_get_fd(imsg);
553 	log_debug("open(%s) returned fd %d", oreq->path, fd);
554 
555 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
556 		if (namespace_has_referrals(ns))
557 			continue;
558 		if (strcmp(oreq->path, ns->data_path) == 0) {
559 			namespace_set_data_fd(ns, fd);
560 			break;
561 		}
562 		if (strcmp(oreq->path, ns->indx_path) == 0) {
563 			namespace_set_indx_fd(ns, fd);
564 			break;
565 		}
566 	}
567 
568 	if (ns == NULL) {
569 		log_warnx("spurious open result");
570 		close(fd);
571 	} else
572 		namespace_queue_schedule(ns, 0);
573 }
574 
575