xref: /openbsd-src/usr.sbin/ldapd/control.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: control.c,v 1.18 2023/03/08 04:43:13 guenther Exp $	*/
25d465952Smartinh 
35d465952Smartinh /*
45d465952Smartinh  * Copyright (c) 2010 Martin Hedenfalk <martin@bzero.se>
55d465952Smartinh  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
65d465952Smartinh  *
75d465952Smartinh  * Permission to use, copy, modify, and distribute this software for any
85d465952Smartinh  * purpose with or without fee is hereby granted, provided that the above
95d465952Smartinh  * copyright notice and this permission notice appear in all copies.
105d465952Smartinh  *
115d465952Smartinh  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
125d465952Smartinh  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
135d465952Smartinh  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
145d465952Smartinh  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
155d465952Smartinh  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
165d465952Smartinh  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
175d465952Smartinh  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
185d465952Smartinh  */
195d465952Smartinh 
205d465952Smartinh #include <sys/queue.h>
215d465952Smartinh #include <sys/types.h>
225d465952Smartinh #include <sys/stat.h>
235d465952Smartinh #include <sys/socket.h>
245d465952Smartinh #include <sys/un.h>
255d465952Smartinh #include <sys/tree.h>
265d465952Smartinh 
275d465952Smartinh #include <net/if.h>
285d465952Smartinh 
295d465952Smartinh #include <errno.h>
305d465952Smartinh #include <event.h>
315d465952Smartinh #include <fcntl.h>
325d465952Smartinh #include <signal.h>
335d465952Smartinh #include <stdlib.h>
345d465952Smartinh #include <string.h>
355d465952Smartinh #include <unistd.h>
365d465952Smartinh 
375d465952Smartinh #include "ldapd.h"
38fdd30f56Sbenno #include "log.h"
395d465952Smartinh 
405d465952Smartinh #define	CONTROL_BACKLOG	5
415d465952Smartinh 
42c5fa57f5Sdv struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
435d465952Smartinh 
445d465952Smartinh struct ctl_conn	*control_connbyfd(int);
456f4dd1d6Sderaadt void		 control_close(int, struct control_sock *);
46bb773af3Smartinh static void	 control_imsgev(struct imsgev *iev, int code, struct imsg *imsg);
470d43ad28Sjmatthew static void	 control_needfd(struct imsgev *iev);
485d465952Smartinh 
495d465952Smartinh void
control_init(struct control_sock * cs)505d465952Smartinh control_init(struct control_sock *cs)
515d465952Smartinh {
525d465952Smartinh 	struct sockaddr_un	 sun;
535d465952Smartinh 	int			 fd;
545d465952Smartinh 	mode_t			 old_umask, mode;
555d465952Smartinh 
565d465952Smartinh 	if (cs->cs_name == NULL)
575d465952Smartinh 		return;
585d465952Smartinh 
59576ea306Sjmatthew 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
605d465952Smartinh 		fatal("control_init: socket");
615d465952Smartinh 
6237f4b933Smmcc 	memset(&sun, 0, sizeof(sun));
635d465952Smartinh 	sun.sun_family = AF_UNIX;
645d465952Smartinh 	if (strlcpy(sun.sun_path, cs->cs_name,
655d465952Smartinh 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path))
665d465952Smartinh 		fatalx("control_init: name too long");
675d465952Smartinh 
685d465952Smartinh 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0)
695d465952Smartinh 		fatalx("control socket already listening");
705d465952Smartinh 
715d465952Smartinh 	if (unlink(cs->cs_name) == -1 && errno != ENOENT)
725d465952Smartinh 		fatal("control_init: unlink");
735d465952Smartinh 
745d465952Smartinh 	if (cs->cs_restricted) {
755d465952Smartinh 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
765d465952Smartinh 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
775d465952Smartinh 	} else {
785d465952Smartinh 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
795d465952Smartinh 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
805d465952Smartinh 	}
815d465952Smartinh 
825d465952Smartinh 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
835d465952Smartinh 		(void)umask(old_umask);
845d465952Smartinh 		fatal("control_init: bind");
855d465952Smartinh 	}
865d465952Smartinh 	(void)umask(old_umask);
875d465952Smartinh 
885d465952Smartinh 	if (chmod(cs->cs_name, mode) == -1) {
895d465952Smartinh 		(void)unlink(cs->cs_name);
905d465952Smartinh 		fatal("control_init: chmod");
915d465952Smartinh 	}
925d465952Smartinh 
935d465952Smartinh 	cs->cs_fd = fd;
945d465952Smartinh }
955d465952Smartinh 
965d465952Smartinh void
control_listen(struct control_sock * cs)975d465952Smartinh control_listen(struct control_sock *cs)
985d465952Smartinh {
995d465952Smartinh 	if (cs->cs_name == NULL)
1005d465952Smartinh 		return;
1015d465952Smartinh 
1025d465952Smartinh 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1)
1035d465952Smartinh 		fatal("control_listen: listen");
1045d465952Smartinh 
1056f4dd1d6Sderaadt 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
1065d465952Smartinh 	    control_accept, cs);
1075d465952Smartinh 	event_add(&cs->cs_ev, NULL);
1086f4dd1d6Sderaadt 	evtimer_set(&cs->cs_evt, control_accept, cs);
1095d465952Smartinh }
1105d465952Smartinh 
1115d465952Smartinh void
control_cleanup(struct control_sock * cs)1125d465952Smartinh control_cleanup(struct control_sock *cs)
1135d465952Smartinh {
1145d465952Smartinh 	if (cs->cs_name == NULL)
1155d465952Smartinh 		return;
1166f4dd1d6Sderaadt 	event_del(&cs->cs_ev);
1176f4dd1d6Sderaadt 	event_del(&cs->cs_evt);
1185d465952Smartinh }
1195d465952Smartinh 
1205d465952Smartinh void
control_accept(int listenfd,short event,void * arg)1215d465952Smartinh control_accept(int listenfd, short event, void *arg)
1225d465952Smartinh {
1235d465952Smartinh 	struct control_sock	*cs = arg;
1245d465952Smartinh 	int			 connfd;
1255d465952Smartinh 	socklen_t		 len;
1265d465952Smartinh 	struct sockaddr_un	 sun;
1275d465952Smartinh 	struct ctl_conn		*c;
1285d465952Smartinh 
1296f4dd1d6Sderaadt 	event_add(&cs->cs_ev, NULL);
1306f4dd1d6Sderaadt 	if ((event & EV_TIMEOUT))
1316f4dd1d6Sderaadt 		return;
1326f4dd1d6Sderaadt 
1335d465952Smartinh 	len = sizeof(sun);
1340d43ad28Sjmatthew 	if ((connfd = accept_reserve(listenfd,
1350d43ad28Sjmatthew 	    (struct sockaddr *)&sun, &len, FD_RESERVE)) == -1) {
1366f4dd1d6Sderaadt 		/*
1376f4dd1d6Sderaadt 		 * Pause accept if we are out of file descriptors, or
1386f4dd1d6Sderaadt 		 * libevent will haunt us here too.
1396f4dd1d6Sderaadt 		 */
1406f4dd1d6Sderaadt 		if (errno == ENFILE || errno == EMFILE) {
1416f4dd1d6Sderaadt 			struct timeval evtpause = { 1, 0 };
1426f4dd1d6Sderaadt 
1436f4dd1d6Sderaadt 			event_del(&cs->cs_ev);
1446f4dd1d6Sderaadt 			evtimer_add(&cs->cs_evt, &evtpause);
1456f4dd1d6Sderaadt 		} else if (errno != EWOULDBLOCK && errno != EINTR)
1465d465952Smartinh 			log_warn("control_accept");
1475d465952Smartinh 		return;
1485d465952Smartinh 	}
1495d465952Smartinh 
1505d465952Smartinh 	if ((c = calloc(1, sizeof(*c))) == NULL) {
1515d465952Smartinh 		log_warn("control_accept");
1525d465952Smartinh 		close(connfd);
1535d465952Smartinh 		return;
1545d465952Smartinh 	}
1555d465952Smartinh 
15648318e0bSderaadt 	log_debug("accepted control fd %d", connfd);
1575d465952Smartinh 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
1580d43ad28Sjmatthew 	imsgev_init(&c->iev, connfd, cs, control_imsgev, control_needfd);
1595d465952Smartinh }
1605d465952Smartinh 
1615d465952Smartinh struct ctl_conn *
control_connbyfd(int fd)1625d465952Smartinh control_connbyfd(int fd)
1635d465952Smartinh {
1645d465952Smartinh 	struct ctl_conn	*c;
1655d465952Smartinh 
1664ff7cad5Skrw 	TAILQ_FOREACH(c, &ctl_conns, entry) {
1674ff7cad5Skrw 		if (c->iev.ibuf.fd == fd)
1684ff7cad5Skrw 			break;
1694ff7cad5Skrw 	}
1705d465952Smartinh 
1715d465952Smartinh 	return (c);
1725d465952Smartinh }
1735d465952Smartinh 
1745d465952Smartinh void
control_close(int fd,struct control_sock * cs)1756f4dd1d6Sderaadt control_close(int fd, struct control_sock *cs)
1765d465952Smartinh {
1775d465952Smartinh 	struct ctl_conn	*c;
1785d465952Smartinh 
1795d465952Smartinh 	if ((c = control_connbyfd(fd)) == NULL) {
1805d465952Smartinh 		log_warnx("control_close: fd %d: not found", fd);
1815d465952Smartinh 		return;
1825d465952Smartinh 	}
1835d465952Smartinh 
18448318e0bSderaadt 	log_debug("close control fd %d", c->iev.ibuf.fd);
1855d465952Smartinh 	TAILQ_REMOVE(&ctl_conns, c, entry);
186bb773af3Smartinh 	imsgev_clear(&c->iev);
1876f4dd1d6Sderaadt 
1886f4dd1d6Sderaadt 	/* Some file descriptors are available again. */
1896f4dd1d6Sderaadt 	if (evtimer_pending(&cs->cs_evt, NULL)) {
1906f4dd1d6Sderaadt 		evtimer_del(&cs->cs_evt);
1916f4dd1d6Sderaadt 		event_add(&cs->cs_ev, NULL);
1926f4dd1d6Sderaadt 	}
1936f4dd1d6Sderaadt 
1945d465952Smartinh 	free(c);
1955d465952Smartinh }
1965d465952Smartinh 
1975d465952Smartinh static int
send_stats(struct imsgev * iev)1985d465952Smartinh send_stats(struct imsgev *iev)
1995d465952Smartinh {
2005d465952Smartinh 	struct namespace	*ns;
2015d465952Smartinh 	const struct btree_stat	*st;
2025d465952Smartinh 	struct ns_stat		 nss;
2035d465952Smartinh 
204bb773af3Smartinh 	imsgev_compose(iev, IMSG_CTL_STATS, 0, iev->ibuf.pid, -1,
2055d465952Smartinh 	    &stats, sizeof(stats));
2065d465952Smartinh 
2075d465952Smartinh 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
208901968c2Smartinh 		if (namespace_has_referrals(ns))
209901968c2Smartinh 			continue;
21037f4b933Smmcc 		memset(&nss, 0, sizeof(nss));
2115d465952Smartinh 		strlcpy(nss.suffix, ns->suffix, sizeof(nss.suffix));
2124074e1b0Smartinh 		if ((st = btree_stat(ns->data_db)) != NULL)
2135d465952Smartinh 			bcopy(st, &nss.data_stat, sizeof(nss.data_stat));
2145d465952Smartinh 
2154074e1b0Smartinh 		if ((st = btree_stat(ns->indx_db)) != NULL)
2165d465952Smartinh 			bcopy(st, &nss.indx_stat, sizeof(nss.indx_stat));
2175d465952Smartinh 
218bb773af3Smartinh 		imsgev_compose(iev, IMSG_CTL_NSSTATS, 0, iev->ibuf.pid, -1,
2195d465952Smartinh 		    &nss, sizeof(nss));
2205d465952Smartinh 	}
2215d465952Smartinh 
222bb773af3Smartinh 	imsgev_compose(iev, IMSG_CTL_END, 0, iev->ibuf.pid, -1, NULL, 0);
2235d465952Smartinh 
2245d465952Smartinh 	return 0;
2255d465952Smartinh }
2265d465952Smartinh 
227bb773af3Smartinh static void
control_imsgev(struct imsgev * iev,int code,struct imsg * imsg)228bb773af3Smartinh control_imsgev(struct imsgev *iev, int code, struct imsg *imsg)
2295d465952Smartinh {
230bb773af3Smartinh 	struct control_sock	*cs;
2315d465952Smartinh 	struct ctl_conn		*c;
232bb773af3Smartinh 	int			 fd, verbose;
233bb773af3Smartinh 
234bb773af3Smartinh 	cs = iev->data;
235bb773af3Smartinh 	fd = iev->ibuf.fd;
2365d465952Smartinh 
2375d465952Smartinh 	if ((c = control_connbyfd(fd)) == NULL) {
238bb773af3Smartinh 		log_warnx("%s: fd %d: not found", __func__, fd);
2395d465952Smartinh 		return;
2405d465952Smartinh 	}
2415d465952Smartinh 
242bb773af3Smartinh 	if (code != IMSGEV_IMSG) {
2436f4dd1d6Sderaadt 		control_close(fd, cs);
2445d465952Smartinh 		return;
2455d465952Smartinh 	}
2465d465952Smartinh 
24748318e0bSderaadt 	log_debug("%s: got imsg %d on fd %d", __func__, imsg->hdr.type, fd);
248bb773af3Smartinh 	switch (imsg->hdr.type) {
2495d465952Smartinh 	case IMSG_CTL_STATS:
250bb773af3Smartinh 		if (send_stats(iev) == -1) {
251bb773af3Smartinh 			log_debug("%s: failed to send statistics", __func__);
2526f4dd1d6Sderaadt 			control_close(fd, cs);
2535d465952Smartinh 		}
2545d465952Smartinh 		break;
2555d465952Smartinh 	case IMSG_CTL_LOG_VERBOSE:
256bb773af3Smartinh 		if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(verbose))
2575d465952Smartinh 			break;
2585d465952Smartinh 
259bb773af3Smartinh 		bcopy(imsg->data, &verbose, sizeof(verbose));
260bb773af3Smartinh 		imsgev_compose(iev_ldapd, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
261bb773af3Smartinh 		    &verbose, sizeof(verbose));
2625d465952Smartinh 
263c0785a05Sreyk 		log_setverbose(verbose);
2645d465952Smartinh 		break;
2655d465952Smartinh 	default:
266bb773af3Smartinh 		log_warnx("%s: unexpected imsg %d", __func__, imsg->hdr.type);
2675d465952Smartinh 		break;
2685d465952Smartinh 	}
2695d465952Smartinh }
2705d465952Smartinh 
2710d43ad28Sjmatthew void
control_needfd(struct imsgev * iev)2720d43ad28Sjmatthew control_needfd(struct imsgev *iev)
2730d43ad28Sjmatthew {
2740d43ad28Sjmatthew 	fatal("should never need an fd for control messages");
2750d43ad28Sjmatthew }
2760d43ad28Sjmatthew 
2770d43ad28Sjmatthew int
control_close_any(struct control_sock * cs)2780d43ad28Sjmatthew control_close_any(struct control_sock *cs)
2790d43ad28Sjmatthew {
2800d43ad28Sjmatthew 	struct ctl_conn		*c;
2810d43ad28Sjmatthew 
2820d43ad28Sjmatthew 	c = TAILQ_FIRST(&ctl_conns);
2830d43ad28Sjmatthew 	if (c != NULL) {
2840d43ad28Sjmatthew 		log_warn("closing oldest control connection");
2850d43ad28Sjmatthew 		control_close(c->iev.ibuf.fd, cs);
2860d43ad28Sjmatthew 		return (0);
2870d43ad28Sjmatthew 	}
2880d43ad28Sjmatthew 	log_warn("no control connections to close");
2890d43ad28Sjmatthew 	return (-1);
2900d43ad28Sjmatthew }
291