ldape.c revision 1.8
1/*	$OpenBSD: ldape.c,v 1.8 2010/06/29 21:54:38 martinh 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
35void			 ldape_sig_handler(int fd, short why, void *data);
36void			 ldape_dispatch_ldapd(int fd, short event, void *ptr);
37static void		 ldape_auth_result(struct imsg *imsg);
38static void		 ldape_open_result(struct imsg *imsg);
39
40int			 ldap_starttls(struct request *req);
41void			 send_ldap_extended_response(struct conn *conn,
42				int msgid, unsigned long type,
43				long long result_code,
44				const char *extended_oid);
45
46struct imsgev		*iev_ldapd;
47struct control_sock	 csock;
48
49void
50ldape_sig_handler(int sig, short why, void *data)
51{
52	log_debug("ldape: got signal %d", sig);
53	if (sig == SIGCHLD) {
54		for (;;) {
55			pid_t	 pid;
56			int	 status;
57
58			pid = waitpid(WAIT_ANY, &status, WNOHANG);
59			if (pid <= 0)
60				break;
61		}
62		return;
63	}
64
65	event_loopexit(NULL);
66}
67
68void
69send_ldap_extended_response(struct conn *conn, int msgid, unsigned long type,
70    long long result_code, const char *extended_oid)
71{
72	int			 rc;
73	struct ber_element	*root, *elm;
74	void			*buf;
75
76	log_debug("sending response %u with result %lld", type, result_code);
77
78	if ((root = ber_add_sequence(NULL)) == NULL)
79		goto fail;
80
81	elm = ber_printf_elements(root, "d{tEss",
82	    msgid, BER_CLASS_APP, type, result_code, "", "");
83	if (elm == NULL)
84		goto fail;
85
86	if (extended_oid)
87		elm = ber_add_string(elm, extended_oid);
88
89	rc = ber_write_elements(&conn->ber, root);
90	ber_free_elements(root);
91
92	if (rc < 0)
93		log_warn("failed to create ldap result");
94	else {
95		ber_get_writebuf(&conn->ber, &buf);
96		if (bufferevent_write(conn->bev, buf, rc) != 0)
97			log_warn("failed to send ldap result");
98	}
99
100	return;
101fail:
102	if (root)
103		ber_free_elements(root);
104}
105
106int
107ldap_refer(struct request *req, const char *basedn, struct search *search,
108    struct referrals *refs)
109{
110	struct ber_element	*root, *elm, *ref_root = NULL;
111	struct referral		*ref;
112	long long		 result_code = LDAP_REFERRAL;
113	unsigned long		 type;
114	int			 rc;
115	void			*buf;
116	char			*url, *scope_str = NULL;
117
118	if (req->type == LDAP_REQ_SEARCH)
119		type = LDAP_RES_SEARCH_RESULT;
120	else
121		type = req->type + 1;
122
123	if (search != NULL) {
124		if (search->scope != LDAP_SCOPE_SUBTREE)
125			scope_str = "base";
126		else
127			scope_str = "sub";
128	}
129
130	log_debug("sending referral in response %u on msgid %i", type, req->msgid);
131
132	if ((root = ber_add_sequence(NULL)) == NULL)
133		goto fail;
134
135	if ((elm = ref_root = ber_add_sequence(NULL)) == NULL)
136		goto fail;
137	ber_set_header(ref_root, BER_CLASS_CONTEXT, LDAP_REQ_SEARCH);
138	SLIST_FOREACH(ref, refs, next) {
139		if (search != NULL)
140			asprintf(&url, "%s/%s??%s", ref->url, basedn,
141			    scope_str);
142		else
143			asprintf(&url, "%s/%s", ref->url, basedn);
144		if (url == NULL) {
145			log_warn("asprintf");
146			goto fail;
147		}
148		log_debug("adding referral '%s'", url);
149		elm = ber_add_string(elm, url);
150		free(url);
151		if (elm == NULL)
152			goto fail;
153	}
154
155	elm = ber_printf_elements(root, "d{tEsse",
156	    req->msgid, BER_CLASS_APP, type, result_code, "", "", ref_root);
157	if (elm == NULL)
158		goto fail;
159	ref_root = NULL;
160
161	rc = ber_write_elements(&req->conn->ber, root);
162	ber_free_elements(root);
163
164	if (rc < 0)
165		log_warn("failed to create ldap result");
166	else {
167		ber_get_writebuf(&req->conn->ber, &buf);
168		if (bufferevent_write(req->conn->bev, buf, rc) != 0)
169			log_warn("failed to send ldap result");
170	}
171
172	request_free(req);
173	return LDAP_REFERRAL;
174
175fail:
176	if (root != NULL)
177		ber_free_elements(root);
178	if (ref_root != NULL)
179		ber_free_elements(ref_root);
180	request_free(req);
181	return LDAP_REFERRAL;
182}
183
184void
185send_ldap_result(struct conn *conn, int msgid, unsigned long type,
186    long long result_code)
187{
188	send_ldap_extended_response(conn, msgid, type, result_code, NULL);
189}
190
191int
192ldap_respond(struct request *req, int code)
193{
194	if (code >= 0)
195		send_ldap_result(req->conn, req->msgid, req->type + 1, code);
196	request_free(req);
197	return code;
198}
199
200int
201ldap_abandon(struct request *req)
202{
203	long long	 msgid;
204	struct search	*search;
205
206	if (ber_scanf_elements(req->op, "i", &msgid) != 0) {
207		request_free(req);
208		return -1;	/* protocol error, but don't respond */
209	}
210
211	TAILQ_FOREACH(search, &req->conn->searches, next) {
212		if (search->req->msgid == msgid) {
213			/* unlinks the search from conn->searches */
214			search_close(search);
215			break;
216		}
217	}
218	request_free(req);
219	return -1;
220}
221
222int
223ldap_unbind(struct request *req)
224{
225	log_debug("current bind dn = %s", req->conn->binddn);
226	conn_disconnect(req->conn);
227	request_free(req);
228	return -1;		/* don't send any response */
229}
230
231int
232ldap_starttls(struct request *req)
233{
234	if ((req->conn->listener->flags & F_STARTTLS) == 0) {
235		log_debug("StartTLS not configured for this connection");
236		return LDAP_OPERATIONS_ERROR;
237	}
238
239	req->conn->s_flags |= F_STARTTLS;
240	return LDAP_SUCCESS;
241}
242
243int
244ldap_extended(struct request *req)
245{
246	int			 i, rc = LDAP_PROTOCOL_ERROR;
247	char			*oid = NULL;
248	struct ber_element	*ext_val = NULL;
249	struct {
250		const char	*oid;
251		int (*fn)(struct request *);
252	} extended_ops[] = {
253		{ "1.3.6.1.4.1.1466.20037", ldap_starttls },
254		{ NULL }
255	};
256
257	if (ber_scanf_elements(req->op, "{se", &oid, &ext_val) != 0)
258		goto done;
259
260	log_debug("got extended operation %s", oid);
261	req->op = ext_val;
262
263	for (i = 0; extended_ops[i].oid != NULL; i++) {
264		if (strcmp(oid, extended_ops[i].oid) == 0) {
265			rc = extended_ops[i].fn(req);
266			break;
267		}
268	}
269
270	if (extended_ops[i].fn == NULL)
271		log_warnx("unimplemented extended operation %s", oid);
272
273done:
274	send_ldap_extended_response(req->conn, req->msgid, LDAP_RES_EXTENDED,
275	    rc, oid);
276
277	request_free(req);
278	return 0;
279}
280
281pid_t
282ldape(struct passwd *pw, char *csockpath, int pipe_parent2ldap[2])
283{
284	int			 on = 1;
285	pid_t			 pid;
286	struct namespace	*ns;
287	struct listener		*l;
288	struct sockaddr_un	*sun = NULL;
289	struct event		 ev_sigint;
290	struct event		 ev_sigterm;
291	struct event		 ev_sigchld;
292	struct event		 ev_sighup;
293	char			 host[128];
294
295	TAILQ_INIT(&conn_list);
296
297	pid = fork();
298	if (pid < 0)
299		fatal("ldape: fork");
300	if (pid > 0)
301		return pid;
302
303	setproctitle("ldap server");
304	event_init();
305
306	signal_set(&ev_sigint, SIGINT, ldape_sig_handler, NULL);
307	signal_set(&ev_sigterm, SIGTERM, ldape_sig_handler, NULL);
308	signal_set(&ev_sigchld, SIGCHLD, ldape_sig_handler, NULL);
309	signal_set(&ev_sighup, SIGHUP, ldape_sig_handler, NULL);
310	signal_add(&ev_sigint, NULL);
311	signal_add(&ev_sigterm, NULL);
312	signal_add(&ev_sigchld, NULL);
313	signal_add(&ev_sighup, NULL);
314	signal(SIGPIPE, SIG_IGN);
315
316	close(pipe_parent2ldap[0]);
317
318	/* Initialize parent imsg events. */
319	if ((iev_ldapd = calloc(1, sizeof(struct imsgev))) == NULL)
320		fatal("calloc");
321	imsg_init(&iev_ldapd->ibuf, pipe_parent2ldap[1]);
322	iev_ldapd->handler = ldape_dispatch_ldapd;
323	imsg_event_add(iev_ldapd);
324
325	/* Initialize control socket. */
326	bzero(&csock, sizeof(csock));
327	csock.cs_name = csockpath;
328	control_init(&csock);
329	control_listen(&csock);
330	TAILQ_INIT(&ctl_conns);
331
332	/* Initialize LDAP listeners.
333	 */
334	TAILQ_FOREACH(l, &conf->listeners, entry) {
335		l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0);
336		if (l->fd < 0)
337			fatal("ldape: socket");
338
339		setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
340
341		if (l->ss.ss_family == AF_UNIX) {
342			sun = (struct sockaddr_un *)&l->ss;
343			log_info("listening on %s", sun->sun_path);
344			if (unlink(sun->sun_path) == -1 && errno != ENOENT)
345				fatal("ldape: unlink");
346		} else {
347			print_host(&l->ss, host, sizeof(host));
348			log_info("listening on %s:%d", host, ntohs(l->port));
349		}
350
351		if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) != 0)
352			fatal("ldape: bind");
353		if (listen(l->fd, 20) != 0)
354			fatal("ldape: listen");
355
356		if (l->ss.ss_family == AF_UNIX) {
357			mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
358			if (chmod(sun->sun_path, mode) == -1) {
359				unlink(sun->sun_path);
360				fatal("ldape: chmod");
361			}
362		}
363
364		fd_nonblock(l->fd);
365
366		event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, conn_accept, l);
367		event_add(&l->ev, NULL);
368
369		ssl_setup(conf, l);
370	}
371
372	TAILQ_FOREACH(ns, &conf->namespaces, next) {
373		if (!namespace_has_referrals(ns) && namespace_open(ns) != 0)
374			fatal(ns->suffix);
375	}
376
377	if (pw != NULL) {
378		if (chroot(pw->pw_dir) == -1)
379			fatal("chroot");
380		if (chdir("/") == -1)
381			fatal("chdir(\"/\")");
382
383		if (setgroups(1, &pw->pw_gid) ||
384		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
385		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
386			fatal("cannot drop privileges");
387	}
388
389	log_debug("ldape: entering event loop");
390	event_dispatch();
391
392	while ((ns = TAILQ_FIRST(&conf->namespaces)) != NULL)
393		namespace_remove(ns);
394
395	control_cleanup(&csock);
396
397	log_info("ldape: exiting");
398	_exit(0);
399}
400
401void
402ldape_dispatch_ldapd(int fd, short event, void *ptr)
403{
404	struct imsgev		*iev = ptr;
405	struct imsgbuf		*ibuf;
406	struct imsg		 imsg;
407	ssize_t			 n;
408
409	if (imsg_event_handle(iev, event) != 0)
410		return;
411
412	ibuf = &iev->ibuf;
413	for (;;) {
414		if ((n = imsg_get(ibuf, &imsg)) == -1)
415			fatal("ldape_dispatch_ldapd: imsg_read error");
416		if (n == 0)
417			break;
418
419		switch (imsg.hdr.type) {
420		case IMSG_LDAPD_AUTH_RESULT:
421			ldape_auth_result(&imsg);
422			break;
423		case IMSG_LDAPD_OPEN_RESULT:
424			ldape_open_result(&imsg);
425			break;
426		default:
427			log_debug("ldape_dispatch_ldapd: unexpected imsg %d",
428			    imsg.hdr.type);
429			break;
430		}
431		imsg_free(&imsg);
432	}
433	imsg_event_add(iev);
434}
435
436static void
437ldape_auth_result(struct imsg *imsg)
438{
439	struct conn		*conn;
440	struct auth_res		*ares = imsg->data;
441
442	log_debug("authentication on conn %i/%lld = %d", ares->fd, ares->msgid,
443	    ares->ok);
444	conn = conn_by_fd(ares->fd);
445	if (conn->bind_req != NULL && conn->bind_req->msgid == ares->msgid)
446		ldap_bind_continue(conn, ares->ok);
447	else
448		log_warnx("spurious auth result");
449}
450
451static void
452ldape_open_result(struct imsg *imsg)
453{
454	struct namespace	*ns;
455	struct open_req		*oreq = imsg->data;
456
457	if (imsg->hdr.len != sizeof(*oreq) + IMSG_HEADER_SIZE)
458		fatal("invalid size of open result");
459
460	/* make sure path is null-terminated */
461	oreq->path[MAXPATHLEN] = '\0';
462
463	log_debug("open(%s) returned fd %i", oreq->path, imsg->fd);
464
465	TAILQ_FOREACH(ns, &conf->namespaces, next) {
466		if (strcmp(oreq->path, ns->data_path) == 0) {
467			namespace_set_data_fd(ns, imsg->fd);
468			break;
469		}
470		if (strcmp(oreq->path, ns->indx_path) == 0) {
471			namespace_set_indx_fd(ns, imsg->fd);
472			break;
473		}
474	}
475
476	if (ns == NULL) {
477		log_warnx("spurious open result");
478		close(imsg->fd);
479	} else
480		namespace_queue_schedule(ns);
481}
482
483