1/*	$OpenBSD: ldapd.c,v 1.32 2022/02/10 13:06:46 robert 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/stat.h>
21#include <sys/un.h>
22#include <sys/types.h>
23#include <sys/wait.h>
24
25#include <assert.h>
26#include <bsd_auth.h>
27#include <ctype.h>
28#include <err.h>
29#include <errno.h>
30#include <event.h>
31#include <fcntl.h>
32#include <login_cap.h>
33#include <paths.h>
34#include <signal.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <time.h>
39#include <unistd.h>
40#include <syslog.h>
41
42#include "ldapd.h"
43#include "log.h"
44
45void		 usage(void);
46void		 ldapd_sig_handler(int fd, short why, void *data);
47void		 ldapd_sigchld_handler(int sig, short why, void *data);
48static void	 ldapd_imsgev(struct imsgev *iev, int code, struct imsg *imsg);
49static void	 ldapd_needfd(struct imsgev *iev);
50static void	 ldapd_auth_request(struct imsgev *iev, struct imsg *imsg);
51static void	 ldapd_open_request(struct imsgev *iev, struct imsg *imsg);
52static void	 ldapd_log_verbose(struct imsg *imsg);
53static pid_t	 start_child(enum ldapd_process, char *, int, int, int,
54		    char *, char *);
55
56struct ldapd_stats	 stats;
57pid_t			 ldape_pid;
58char			*datadir = DATADIR;
59
60void
61usage(void)
62{
63	extern char	*__progname;
64
65	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] "
66	    "[-f file] [-r directory] [-s file]\n", __progname);
67	exit(1);
68}
69
70void
71ldapd_sig_handler(int sig, short why, void *data)
72{
73	log_info("ldapd: got signal %d", sig);
74	if (sig == SIGINT || sig == SIGTERM)
75		event_loopexit(NULL);
76}
77
78void
79ldapd_sigchld_handler(int sig, short why, void *data)
80{
81	pid_t		 pid;
82	int		 status;
83
84	while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
85		if (pid == -1) {
86			if (errno == EINTR)
87				continue;
88			if (errno != ECHILD)
89				log_warn("waitpid");
90			break;
91		}
92
93		if (WIFEXITED(status))
94			log_debug("child %d exited with status %d",
95			    pid, WEXITSTATUS(status));
96		else if (WIFSIGNALED(status))
97			log_debug("child %d exited due to signal %d",
98			    pid, WTERMSIG(status));
99		else
100			log_debug("child %d terminated abnormally", pid);
101
102		if (pid == ldape_pid) {
103			log_info("ldapd: lost ldap server");
104			event_loopexit(NULL);
105			break;
106		}
107	}
108}
109
110int
111main(int argc, char *argv[])
112{
113	int			 c;
114	int			 debug = 0, verbose = 0, eflag = 0;
115	int			 configtest = 0;
116	int			 pipe_parent2ldap[2];
117	char			*conffile = CONFFILE;
118	char			*csockpath = LDAPD_SOCKET;
119	char			*saved_argv0;
120	struct imsgev		*iev_ldape;
121	struct event		 ev_sigint;
122	struct event		 ev_sigterm;
123	struct event		 ev_sigchld;
124	struct event		 ev_sighup;
125	struct stat		 sb;
126
127	log_init(1, LOG_DAEMON);	/* log to stderr until daemonized */
128
129	saved_argv0 = argv[0];
130	if (saved_argv0 == NULL)
131		saved_argv0 = "ldapd";
132
133	while ((c = getopt(argc, argv, "dhvD:f:nr:s:E")) != -1) {
134
135		switch (c) {
136		case 'd':
137			debug = 1;
138			break;
139		case 'D':
140			if (cmdline_symset(optarg) < 0) {
141				warnx("could not parse macro definition %s",
142				    optarg);
143			}
144			break;
145		case 'f':
146			conffile = optarg;
147			break;
148		case 'h':
149			usage();
150			/* NOTREACHED */
151		case 'n':
152			configtest = 1;
153			break;
154		case 'r':
155			datadir = optarg;
156			break;
157		case 's':
158			csockpath = optarg;
159			break;
160		case 'v':
161			verbose++;
162			break;
163		case 'E':
164			eflag = 1;
165			break;
166		default:
167			usage();
168			/* NOTREACHED */
169		}
170	}
171
172	argc -= optind;
173	if (argc > 0)
174		usage();
175
176	/* check for root privileges  */
177	if (geteuid())
178		errx(1, "need root privileges");
179
180	/* check for ldapd user */
181	if (getpwnam(LDAPD_USER) == NULL)
182		errx(1, "unknown user %s", LDAPD_USER);
183
184	log_setverbose(verbose);
185	stats.started_at = time(0);
186
187	if (parse_config(conffile) != 0)
188		exit(2);
189
190	if (configtest) {
191		fprintf(stderr, "configuration ok\n");
192		exit(0);
193	}
194
195	log_init(debug, LOG_DAEMON);
196
197	if (eflag)
198		ldape(debug, verbose, csockpath);
199
200	if (stat(datadir, &sb) == -1)
201		err(1, "%s", datadir);
202	if (!S_ISDIR(sb.st_mode))
203		errx(1, "%s is not a directory", datadir);
204
205	if (!debug) {
206		if (daemon(1, 0) == -1)
207			err(1, "failed to daemonize");
208	}
209
210	log_info("startup");
211
212	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
213	    PF_UNSPEC, pipe_parent2ldap) != 0)
214		fatal("socketpair");
215
216	ldape_pid = start_child(PROC_LDAP_SERVER, saved_argv0,
217	    pipe_parent2ldap[1], debug, verbose, csockpath, conffile);
218
219	ldap_loginit("auth", debug, verbose);
220	setproctitle("auth");
221	event_init();
222
223	signal_set(&ev_sigint, SIGINT, ldapd_sig_handler, NULL);
224	signal_set(&ev_sigterm, SIGTERM, ldapd_sig_handler, NULL);
225	signal_set(&ev_sigchld, SIGCHLD, ldapd_sigchld_handler, NULL);
226	signal_set(&ev_sighup, SIGHUP, ldapd_sig_handler, NULL);
227	signal_add(&ev_sigint, NULL);
228	signal_add(&ev_sigterm, NULL);
229	signal_add(&ev_sigchld, NULL);
230	signal_add(&ev_sighup, NULL);
231	signal(SIGPIPE, SIG_IGN);
232
233	if ((iev_ldape = calloc(1, sizeof(struct imsgev))) == NULL)
234		fatal("calloc");
235	imsgev_init(iev_ldape, pipe_parent2ldap[0], NULL, ldapd_imsgev,
236	    ldapd_needfd);
237
238	if (unveil(_PATH_NOLOGIN, "r") == -1)
239		err(1, "unveil %s", _PATH_NOLOGIN);
240	if (unveil(_PATH_LOGIN_CONF, "r") == -1)
241		err(1, "unveil %s", _PATH_LOGIN_CONF);
242	if (unveil(_PATH_LOGIN_CONF ".db", "r") == -1)
243		err(1, "unveil %s.db", _PATH_LOGIN_CONF);
244	if (unveil(_PATH_LOGIN_CONF_D, "r") == -1)
245		err(1, "unveil %s", _PATH_LOGIN_CONF_D);
246	if (unveil(_PATH_AUTHPROGDIR, "x") == -1)
247		err(1, "unveil %s", _PATH_AUTHPROGDIR);
248	if (unveil(datadir, "rwc") == -1)
249		err(1, "unveil %s", datadir);
250	if (unveil(NULL, NULL) == -1)
251		err(1, "unveil");
252
253	if (pledge("stdio rpath wpath cpath getpw sendfd proc exec",
254	    NULL) == -1)
255		err(1, "pledge");
256
257	event_dispatch();
258
259	log_debug("ldapd: exiting");
260
261	return 0;
262}
263
264static void
265ldapd_imsgev(struct imsgev *iev, int code, struct imsg *imsg)
266{
267	switch (code) {
268	case IMSGEV_IMSG:
269		log_debug("%s: got imsg %d on fd %d",
270		    __func__, imsg->hdr.type, iev->ibuf.fd);
271		switch (imsg->hdr.type) {
272		case IMSG_LDAPD_AUTH:
273			ldapd_auth_request(iev, imsg);
274			break;
275		case IMSG_CTL_LOG_VERBOSE:
276			ldapd_log_verbose(imsg);
277			break;
278		case IMSG_LDAPD_OPEN:
279			ldapd_open_request(iev, imsg);
280			break;
281		default:
282			log_debug("%s: unexpected imsg %d",
283			    __func__, imsg->hdr.type);
284			break;
285		}
286		break;
287	case IMSGEV_EREAD:
288	case IMSGEV_EWRITE:
289	case IMSGEV_EIMSG:
290		fatal("imsgev read/write error");
291		break;
292	case IMSGEV_DONE:
293		event_loopexit(NULL);
294		break;
295	}
296}
297
298static void
299ldapd_needfd(struct imsgev *iev)
300{
301	fatal("should never need an fd for parent messages");
302}
303
304static int
305ldapd_auth_classful(char *name, char *password)
306{
307	login_cap_t		*lc = NULL;
308	char			*class = NULL, *style = NULL;
309	auth_session_t		*as;
310
311	if ((class = strchr(name, '#')) == NULL) {
312		log_debug("regular auth");
313		return auth_userokay(name, NULL, "auth-ldap", password);
314	}
315	*class++ = '\0';
316
317	if ((lc = login_getclass(class)) == NULL) {
318		log_debug("login_getclass(%s) for [%s] failed", class, name);
319		return 0;
320	}
321	if ((style = login_getstyle(lc, style, "auth-ldap")) == NULL) {
322		log_debug("login_getstyle() for [%s] failed", name);
323		login_close(lc);
324		return 0;
325	}
326	if (password) {
327		if ((as = auth_open()) == NULL) {
328			login_close(lc);
329			return 0;
330		}
331		auth_setitem(as, AUTHV_SERVICE, "response");
332		auth_setdata(as, "", 1);
333		auth_setdata(as, password, strlen(password) + 1);
334		explicit_bzero(password, strlen(password));
335	} else
336		as = NULL;
337
338	as = auth_verify(as, style, name, lc->lc_class, (char *)NULL);
339	login_close(lc);
340	return (as != NULL ? auth_close(as) : 0);
341}
342
343static void
344ldapd_auth_request(struct imsgev *iev, struct imsg *imsg)
345{
346	struct auth_req		*areq = imsg->data;
347	struct auth_res		 ares;
348
349	if (imsg->hdr.len != sizeof(*areq) + IMSG_HEADER_SIZE)
350		fatal("invalid size of auth request");
351
352	/* make sure name and password are null-terminated */
353	areq->name[sizeof(areq->name) - 1] = '\0';
354	areq->password[sizeof(areq->password) - 1] = '\0';
355
356	log_debug("authenticating [%s]", areq->name);
357	ares.ok = ldapd_auth_classful(areq->name, areq->password);
358	ares.fd = areq->fd;
359	ares.msgid = areq->msgid;
360	memset(areq, 0, sizeof(*areq));
361	imsgev_compose(iev, IMSG_LDAPD_AUTH_RESULT, 0, 0, -1, &ares,
362	    sizeof(ares));
363}
364
365static void
366ldapd_log_verbose(struct imsg *imsg)
367{
368	int	 verbose;
369
370	if (imsg->hdr.len != sizeof(verbose) + IMSG_HEADER_SIZE)
371		fatal("invalid size of log verbose request");
372
373	bcopy(imsg->data, &verbose, sizeof(verbose));
374	log_setverbose(verbose);
375}
376
377static void
378ldapd_open_request(struct imsgev *iev, struct imsg *imsg)
379{
380	struct open_req		*oreq = imsg->data;
381	int			 oflags, fd;
382
383	if (imsg->hdr.len != sizeof(*oreq) + IMSG_HEADER_SIZE)
384		fatal("invalid size of open request");
385
386	if (oreq->path[PATH_MAX-1] != '\0')
387		fatal("bogus path");
388
389	if (strncmp(oreq->path, datadir, strlen(datadir)) != 0) {
390		log_warnx("refusing to open file %s", oreq->path);
391		fatal("ldape sent invalid open request");
392	}
393
394	if (oreq->rdonly)
395		oflags = O_RDONLY;
396	else
397		oflags = O_RDWR | O_CREAT | O_APPEND;
398
399	log_debug("opening [%s]", oreq->path);
400	fd = open(oreq->path, oflags | O_NOFOLLOW, 0600);
401	if (fd == -1)
402		log_warn("%s", oreq->path);
403
404	imsgev_compose(iev, IMSG_LDAPD_OPEN_RESULT, 0, 0, fd, oreq,
405	    sizeof(*oreq));
406}
407
408static pid_t
409start_child(enum ldapd_process p, char *argv0, int fd, int debug,
410    int verbose, char *csockpath, char *conffile)
411{
412	char		*argv[11];
413	int		 argc = 0;
414	pid_t		 pid;
415
416	switch (pid = fork()) {
417	case -1:
418		fatal("cannot fork");
419	case 0:
420		break;
421	default:
422		close(fd);
423		return (pid);
424	}
425
426	if (fd != PROC_PARENT_SOCK_FILENO) {
427		if (dup2(fd, PROC_PARENT_SOCK_FILENO) == -1)
428			fatal("cannot setup imsg fd");
429	} else if (fcntl(fd, F_SETFD, 0) == -1)
430		fatal("cannot setup imsg fd");
431
432	argv[argc++] = argv0;
433	switch (p) {
434	case PROC_MAIN_AUTH:
435		fatalx("Can not start main process");
436	case PROC_LDAP_SERVER:
437		argv[argc++] = "-E";
438		break;
439	}
440	if (debug)
441		argv[argc++] = "-d";
442	if (verbose >= 3)
443		argv[argc++] = "-vvv";
444	else if (verbose == 2)
445		argv[argc++] = "-vv";
446	else if (verbose == 1)
447		argv[argc++] = "-v";
448	if (csockpath) {
449		argv[argc++] = "-s";
450		argv[argc++] = csockpath;
451	}
452	if (conffile) {
453		argv[argc++] = "-f";
454		argv[argc++] = conffile;
455	}
456	if (datadir) {
457		argv[argc++] = "-r";
458		argv[argc++] = datadir;
459	}
460
461	argv[argc++] = NULL;
462
463	execvp(argv0, argv);
464	fatal("execvp");
465}
466