xref: /openbsd-src/usr.sbin/ldapd/conn.c (revision d2e51e448f98efc84683e91121cbee3dcd9efca1)
1*d2e51e44Sclaudio /*	$OpenBSD: conn.c,v 1.21 2023/06/26 10:28:12 claudio Exp $ */
25d465952Smartinh 
35d465952Smartinh /*
45d465952Smartinh  * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
55d465952Smartinh  *
65d465952Smartinh  * Permission to use, copy, modify, and distribute this software for any
75d465952Smartinh  * purpose with or without fee is hereby granted, provided that the above
85d465952Smartinh  * copyright notice and this permission notice appear in all copies.
95d465952Smartinh  *
105d465952Smartinh  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115d465952Smartinh  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125d465952Smartinh  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135d465952Smartinh  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145d465952Smartinh  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155d465952Smartinh  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165d465952Smartinh  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175d465952Smartinh  */
185d465952Smartinh 
195d465952Smartinh #include <sys/queue.h>
205d465952Smartinh #include <sys/types.h>
215d465952Smartinh 
225d465952Smartinh #include <stdlib.h>
235d465952Smartinh #include <errno.h>
245d465952Smartinh #include <unistd.h>
255d465952Smartinh 
265d465952Smartinh #include "ldapd.h"
27fdd30f56Sbenno #include "log.h"
285d465952Smartinh 
295d465952Smartinh int			 conn_dispatch(struct conn *conn);
3057f46873Sjmatthew int			 conn_tls_init(struct conn *);
31f9444383Sclaudio unsigned int		 ldap_application(struct ber_element *elm);
325d465952Smartinh 
335d465952Smartinh struct conn_list	 conn_list;
345d465952Smartinh 
35f9444383Sclaudio unsigned int
ldap_application(struct ber_element * elm)365d465952Smartinh ldap_application(struct ber_element *elm)
375d465952Smartinh {
385d465952Smartinh 	return BER_TYPE_OCTETSTRING;
395d465952Smartinh }
405d465952Smartinh 
415d465952Smartinh void
request_free(struct request * req)425d465952Smartinh request_free(struct request *req)
435d465952Smartinh {
445d465952Smartinh 	if (req->root != NULL)
45696b5899Stb 		ober_free_elements(req->root);
465d465952Smartinh 	free(req);
475d465952Smartinh }
485d465952Smartinh 
495d465952Smartinh void
conn_close(struct conn * conn)505d465952Smartinh conn_close(struct conn *conn)
515d465952Smartinh {
525d465952Smartinh 	struct search	*search, *next;
536f4dd1d6Sderaadt 	struct listener *l = conn->listener;
545d465952Smartinh 
555d465952Smartinh 	log_debug("closing connection %d", conn->fd);
565d465952Smartinh 
575d465952Smartinh 	/* Cancel any ongoing searches on this connection. */
585d465952Smartinh 	for (search = TAILQ_FIRST(&conn->searches); search; search = next) {
595d465952Smartinh 		next = TAILQ_NEXT(search, next);
605d465952Smartinh 		search_close(search);
615d465952Smartinh 	}
625d465952Smartinh 
635d465952Smartinh 	/* Cancel any queued requests on this connection. */
645d465952Smartinh 	namespace_cancel_conn(conn);
655d465952Smartinh 
6657f46873Sjmatthew 	tls_free(conn->tls);
675d465952Smartinh 
685d465952Smartinh 	TAILQ_REMOVE(&conn_list, conn, next);
69696b5899Stb 	ober_free(&conn->ber);
705d465952Smartinh 	if (conn->bev != NULL)
715d465952Smartinh 		bufferevent_free(conn->bev);
725d465952Smartinh 	close(conn->fd);
736f4dd1d6Sderaadt 
746f4dd1d6Sderaadt 	/* Some file descriptors are available again. */
756f4dd1d6Sderaadt 	if (evtimer_pending(&l->evt, NULL)) {
766f4dd1d6Sderaadt 		evtimer_del(&l->evt);
776f4dd1d6Sderaadt 		event_add(&l->ev, NULL);
786f4dd1d6Sderaadt 	}
796f4dd1d6Sderaadt 
805d465952Smartinh 	free(conn->binddn);
8102cddd88Smartinh 	free(conn->pending_binddn);
825d465952Smartinh 	free(conn);
835d465952Smartinh 
845d465952Smartinh 	--stats.conns;
855d465952Smartinh }
865d465952Smartinh 
875d465952Smartinh /* Marks a connection for disconnect. The connection will be closed when
885d465952Smartinh  * any remaining data has been flushed to the socket.
895d465952Smartinh  */
905d465952Smartinh void
conn_disconnect(struct conn * conn)915d465952Smartinh conn_disconnect(struct conn *conn)
925d465952Smartinh {
935d465952Smartinh 	conn->disconnect = 1;
945d465952Smartinh 	bufferevent_enable(conn->bev, EV_WRITE);
955d465952Smartinh }
965d465952Smartinh 
971d2cc1d9Smartinh void
request_dispatch(struct request * req)985d465952Smartinh request_dispatch(struct request *req)
995d465952Smartinh {
1005d465952Smartinh 	unsigned long		 i;
1015d465952Smartinh 	struct {
102f9444383Sclaudio 		unsigned int	 type;
1035d465952Smartinh 		int (*fn)(struct request *);
1045d465952Smartinh 	} requests[] = {
1055d465952Smartinh 		{ LDAP_REQ_SEARCH,	ldap_search },
1065d465952Smartinh 		{ LDAP_REQ_BIND,	ldap_bind },
1075772b996Smartinh 		{ LDAP_REQ_COMPARE,	ldap_compare },
1085d465952Smartinh 		{ LDAP_REQ_ADD,		ldap_add },
1095d465952Smartinh 		{ LDAP_REQ_UNBIND_30,	ldap_unbind },
1105d465952Smartinh 		{ LDAP_REQ_MODIFY,	ldap_modify },
1115d465952Smartinh 		{ LDAP_REQ_ABANDON_30,	ldap_abandon },
1125d465952Smartinh 		{ LDAP_REQ_DELETE_30,	ldap_delete },
1135d465952Smartinh 		{ LDAP_REQ_EXTENDED,	ldap_extended },
1145d465952Smartinh 		{ 0,			NULL }
1155d465952Smartinh 	};
1165d465952Smartinh 
1175d465952Smartinh 	/* RFC4511, section 4.2.1 says we shouldn't process other requests
1185d465952Smartinh 	 * while binding. A bind operation can, however, be aborted by sending
1195d465952Smartinh 	 * another bind operation.
1205d465952Smartinh 	 */
1215d465952Smartinh 	if (req->conn->bind_req != NULL && req->type != LDAP_REQ_BIND) {
1225d465952Smartinh 		log_warnx("got request while bind in progress");
1235d465952Smartinh 		ldap_respond(req, LDAP_SASL_BIND_IN_PROGRESS);
1241d2cc1d9Smartinh 		return;
1255d465952Smartinh 	}
1265d465952Smartinh 
1275d465952Smartinh 	for (i = 0; requests[i].fn != NULL; i++) {
1285d465952Smartinh 		if (requests[i].type == req->type) {
1295d465952Smartinh 			requests[i].fn(req);
1305d465952Smartinh 			break;
1315d465952Smartinh 		}
1325d465952Smartinh 	}
1335d465952Smartinh 
1345d465952Smartinh 	if (requests[i].fn == NULL) {
135f9444383Sclaudio 		log_warnx("unhandled request %u (not implemented)", req->type);
1365d465952Smartinh 		ldap_respond(req, LDAP_PROTOCOL_ERROR);
1375d465952Smartinh 	}
1385d465952Smartinh }
1395d465952Smartinh 
1405d465952Smartinh int
conn_dispatch(struct conn * conn)1415d465952Smartinh conn_dispatch(struct conn *conn)
1425d465952Smartinh {
1435d465952Smartinh 	int			 class;
1445d465952Smartinh 	struct request		*req;
145b7a178a5Smartinh 	u_char			*rptr;
1465d465952Smartinh 
1475d465952Smartinh 	++stats.requests;
1485d465952Smartinh 
1495d465952Smartinh 	if ((req = calloc(1, sizeof(*req))) == NULL) {
1505d465952Smartinh 		log_warn("calloc");
1515d465952Smartinh 		conn_disconnect(conn);
1525d465952Smartinh 		return -1;
1535d465952Smartinh 	}
1545d465952Smartinh 
1555d465952Smartinh 	req->conn = conn;
156b7a178a5Smartinh 	rptr = conn->ber.br_rptr;	/* save where we start reading */
1575d465952Smartinh 
158696b5899Stb 	if ((req->root = ober_read_elements(&conn->ber, NULL)) == NULL) {
1595d465952Smartinh 		if (errno != ECANCELED) {
1605d465952Smartinh 			log_warnx("protocol error");
161b7a178a5Smartinh 			hexdump(rptr, conn->ber.br_rend - rptr,
162b7a178a5Smartinh 			    "failed to parse request from %zi bytes:",
163b7a178a5Smartinh 			    conn->ber.br_rend - rptr);
1645d465952Smartinh 			conn_disconnect(conn);
1655d465952Smartinh 		}
1665d465952Smartinh 		request_free(req);
1675d465952Smartinh 		return -1;
1685d465952Smartinh 	}
169c0785a05Sreyk 	log_debug("consumed %ld bytes", conn->ber.br_rptr - rptr);
1705d465952Smartinh 
1715d465952Smartinh 	/* Read message id and request type.
1725d465952Smartinh 	 */
173696b5899Stb 	if (ober_scanf_elements(req->root, "{ite",
1745d465952Smartinh 	    &req->msgid, &class, &req->type, &req->op) != 0) {
1755d465952Smartinh 		log_warnx("protocol error");
176b7a178a5Smartinh 		ldap_debug_elements(req->root, -1,
177b7a178a5Smartinh 		    "received invalid request on fd %d", conn->fd);
1785d465952Smartinh 		conn_disconnect(conn);
1795d465952Smartinh 		request_free(req);
1805d465952Smartinh 		return -1;
1815d465952Smartinh 	}
1825d465952Smartinh 
183b7a178a5Smartinh 	ldap_debug_elements(req->root, req->type,
184b7a178a5Smartinh 	    "received request on fd %d", conn->fd);
185b7a178a5Smartinh 
186f9444383Sclaudio 	log_debug("got request type %u, id %lld", req->type, req->msgid);
1875d465952Smartinh 	request_dispatch(req);
1885d465952Smartinh 	return 0;
1895d465952Smartinh }
1905d465952Smartinh 
1915d465952Smartinh void
conn_read(struct bufferevent * bev,void * data)1925d465952Smartinh conn_read(struct bufferevent *bev, void *data)
1935d465952Smartinh {
1945d465952Smartinh 	size_t			 nused = 0;
1955d465952Smartinh 	struct conn		*conn = data;
1965d465952Smartinh 	struct evbuffer		*input;
1975d465952Smartinh 
1985d465952Smartinh 	input = EVBUFFER_INPUT(bev);
199696b5899Stb 	ober_set_readbuf(&conn->ber,
2005d465952Smartinh 	    EVBUFFER_DATA(input), EVBUFFER_LENGTH(input));
2015d465952Smartinh 
2025d465952Smartinh 	while (conn->ber.br_rend - conn->ber.br_rptr > 0) {
2035d465952Smartinh 		if (conn_dispatch(conn) == 0)
2045a683acbSmartinh 			nused = conn->ber.br_rptr - conn->ber.br_rbuf;
2055d465952Smartinh 		else
2065d465952Smartinh 			break;
2075d465952Smartinh 	}
2085d465952Smartinh 
2095d465952Smartinh 	evbuffer_drain(input, nused);
2105d465952Smartinh }
2115d465952Smartinh 
2125d465952Smartinh void
conn_write(struct bufferevent * bev,void * data)2135d465952Smartinh conn_write(struct bufferevent *bev, void *data)
2145d465952Smartinh {
2155d465952Smartinh 	struct search	*search, *next;
2165d465952Smartinh 	struct conn	*conn = data;
2175d465952Smartinh 
2185d465952Smartinh 	/* Continue any ongoing searches.
2195d465952Smartinh 	 * Note that the search may be unlinked and freed by conn_search.
2205d465952Smartinh 	 */
2215d465952Smartinh 	for (search = TAILQ_FIRST(&conn->searches); search; search = next) {
2225d465952Smartinh 		next = TAILQ_NEXT(search, next);
2235d465952Smartinh 		conn_search(search);
2245d465952Smartinh 	}
2255d465952Smartinh 
2265d465952Smartinh 	if (conn->disconnect)
2275d465952Smartinh 		conn_close(conn);
2285d465952Smartinh 	else if (conn->s_flags & F_STARTTLS) {
2295d465952Smartinh 		conn->s_flags &= ~F_STARTTLS;
23057f46873Sjmatthew 		if (conn_tls_init(conn) == -1)
23157f46873Sjmatthew 			conn_close(conn);
2325d465952Smartinh 	}
2335d465952Smartinh }
2345d465952Smartinh 
2355d465952Smartinh void
conn_err(struct bufferevent * bev,short why,void * data)2365d465952Smartinh conn_err(struct bufferevent *bev, short why, void *data)
2375d465952Smartinh {
2385d465952Smartinh 	struct conn	*conn = data;
2395d465952Smartinh 
2405d465952Smartinh 	if ((why & EVBUFFER_EOF) == EVBUFFER_EOF)
24148318e0bSderaadt 		log_debug("end-of-file on connection %d", conn->fd);
2425d465952Smartinh 	else if ((why & EVBUFFER_TIMEOUT) == EVBUFFER_TIMEOUT)
24348318e0bSderaadt 		log_debug("timeout on connection %d", conn->fd);
2445d465952Smartinh 	else
245*d2e51e44Sclaudio 		log_warn("%s error on connection %d",
246*d2e51e44Sclaudio 		    why & EVBUFFER_WRITE ? "write" : "read", conn->fd);
2475d465952Smartinh 
2485d465952Smartinh 	conn_close(conn);
2495d465952Smartinh }
2505d465952Smartinh 
2515d465952Smartinh void
conn_accept(int fd,short event,void * data)2526f4dd1d6Sderaadt conn_accept(int fd, short event, void *data)
2535d465952Smartinh {
2545d465952Smartinh 	int			 afd;
2555d465952Smartinh 	socklen_t		 addrlen;
2565d465952Smartinh 	struct conn		*conn;
2575d465952Smartinh 	struct listener		*l = data;
2585d465952Smartinh 	struct sockaddr_storage	 remote_addr;
2595d465952Smartinh 	char			 host[128];
2605d465952Smartinh 
2616f4dd1d6Sderaadt 	event_add(&l->ev, NULL);
2626f4dd1d6Sderaadt 	if ((event & EV_TIMEOUT))
2636f4dd1d6Sderaadt 		return;
2646f4dd1d6Sderaadt 
2655d465952Smartinh 	addrlen = sizeof(remote_addr);
2660d43ad28Sjmatthew 	afd = accept_reserve(fd, (struct sockaddr *)&remote_addr, &addrlen,
2670d43ad28Sjmatthew 	    FD_RESERVE);
2685d465952Smartinh 	if (afd == -1) {
2696f4dd1d6Sderaadt 		/*
2706f4dd1d6Sderaadt 		 * Pause accept if we are out of file descriptors, or
2716f4dd1d6Sderaadt 		 * libevent will haunt us here too.
2726f4dd1d6Sderaadt 		 */
2736f4dd1d6Sderaadt 		if (errno == ENFILE || errno == EMFILE) {
2746f4dd1d6Sderaadt 			struct timeval evtpause = { 1, 0 };
2756f4dd1d6Sderaadt 
2766f4dd1d6Sderaadt 			event_del(&l->ev);
2776f4dd1d6Sderaadt 			evtimer_add(&l->evt, &evtpause);
2786f4dd1d6Sderaadt 		} else if (errno != EWOULDBLOCK && errno != EINTR)
2796f4dd1d6Sderaadt 			log_warn("conn_accept");
2805d465952Smartinh 		return;
2815d465952Smartinh 	}
2825d465952Smartinh 
2835d465952Smartinh 	if (l->ss.ss_family == AF_UNIX) {
2845d465952Smartinh 		uid_t		 euid;
2855d465952Smartinh 		gid_t		 egid;
2865d465952Smartinh 
2875d465952Smartinh 		if (getpeereid(afd, &euid, &egid) == -1)
2885d465952Smartinh 			log_warnx("conn_accept: getpeereid");
2895d465952Smartinh 		else
2905d465952Smartinh 			log_debug("accepted local connection by uid %d", euid);
2915d465952Smartinh 	} else {
2925d465952Smartinh 		print_host(&remote_addr, host, sizeof(host));
2935d465952Smartinh 		log_debug("accepted connection from %s on fd %d", host, afd);
2945d465952Smartinh 	}
2955d465952Smartinh 
2965d465952Smartinh 	if ((conn = calloc(1, sizeof(*conn))) == NULL) {
2975d465952Smartinh 		log_warn("malloc");
2986f4dd1d6Sderaadt 		goto giveup;
2995d465952Smartinh 	}
300696b5899Stb 	ober_set_application(&conn->ber, ldap_application);
3015d465952Smartinh 	conn->fd = afd;
302b7f53b5aSmartinh 	conn->listener = l;
3035d465952Smartinh 
3045d465952Smartinh 	conn->bev = bufferevent_new(afd, conn_read, conn_write,
3055d465952Smartinh 	    conn_err, conn);
3065d465952Smartinh 	if (conn->bev == NULL) {
3075d465952Smartinh 		log_warn("conn_accept: bufferevent_new");
3085d465952Smartinh 		free(conn);
3096f4dd1d6Sderaadt 		goto giveup;
3105d465952Smartinh 	}
3115d465952Smartinh 	bufferevent_enable(conn->bev, EV_READ);
3125d465952Smartinh 	bufferevent_settimeout(conn->bev, 0, 60);
31357f46873Sjmatthew 	if (l->flags & F_LDAPS)
314622ead63Sjsg 		if (conn_tls_init(conn) == -1) {
31557f46873Sjmatthew 			conn_close(conn);
316622ead63Sjsg 			goto giveup;
317622ead63Sjsg 		}
3185d465952Smartinh 
3195d465952Smartinh 	TAILQ_INIT(&conn->searches);
3205d465952Smartinh 	TAILQ_INSERT_HEAD(&conn_list, conn, next);
3215d465952Smartinh 
3225d465952Smartinh 	if (l->flags & F_SECURE)
3235d465952Smartinh 		conn->s_flags |= F_SECURE;
3245d465952Smartinh 
3255d465952Smartinh 	++stats.conns;
3266f4dd1d6Sderaadt 	return;
3276f4dd1d6Sderaadt giveup:
3286f4dd1d6Sderaadt 	close(afd);
3296f4dd1d6Sderaadt 	/* Some file descriptors are available again. */
3306f4dd1d6Sderaadt 	if (evtimer_pending(&l->evt, NULL)) {
3316f4dd1d6Sderaadt 		evtimer_del(&l->evt);
3326f4dd1d6Sderaadt 		event_add(&l->ev, NULL);
3336f4dd1d6Sderaadt 	}
3345d465952Smartinh }
3355d465952Smartinh 
3365d465952Smartinh struct conn *
conn_by_fd(int fd)3375d465952Smartinh conn_by_fd(int fd)
3385d465952Smartinh {
3395d465952Smartinh 	struct conn		*conn;
3405d465952Smartinh 
3415d465952Smartinh 	TAILQ_FOREACH(conn, &conn_list, next) {
3425d465952Smartinh 		if (conn->fd == fd)
3435d465952Smartinh 			return conn;
3445d465952Smartinh 	}
3455d465952Smartinh 	return NULL;
3465d465952Smartinh }
3475d465952Smartinh 
3480d43ad28Sjmatthew int
conn_close_any(void)3493fe2f2e6Stb conn_close_any(void)
3500d43ad28Sjmatthew {
3510d43ad28Sjmatthew 	struct conn		*conn;
3520d43ad28Sjmatthew 
3530d43ad28Sjmatthew 	/* Close oldest idle connection */
3540d43ad28Sjmatthew 	TAILQ_FOREACH_REVERSE(conn, &conn_list, conn_list, next) {
3550d43ad28Sjmatthew 		if (namespace_conn_queue_count(conn) == 0) {
3560d43ad28Sjmatthew 			conn_close(conn);
3570d43ad28Sjmatthew 			return 0;
3580d43ad28Sjmatthew 		}
3590d43ad28Sjmatthew 	}
3600d43ad28Sjmatthew 
3610d43ad28Sjmatthew 	/* Close oldest connection */
3620d43ad28Sjmatthew 	conn = TAILQ_LAST(&conn_list, conn_list);
3630d43ad28Sjmatthew 	if (conn != NULL) {
3640d43ad28Sjmatthew 		conn_close(conn);
3650d43ad28Sjmatthew 		return 0;
3660d43ad28Sjmatthew 	}
3670d43ad28Sjmatthew 
3680d43ad28Sjmatthew 	return -1;
3690d43ad28Sjmatthew }
37057f46873Sjmatthew 
37157f46873Sjmatthew int
conn_tls_init(struct conn * conn)37257f46873Sjmatthew conn_tls_init(struct conn *conn)
37357f46873Sjmatthew {
37457f46873Sjmatthew 	struct listener *l = conn->listener;
37557f46873Sjmatthew 
37657f46873Sjmatthew 	if (!(l->flags & F_SSL))
37757f46873Sjmatthew 		return 0;
37857f46873Sjmatthew 
37957f46873Sjmatthew 	log_debug("conn_tls_init: switching to TLS");
38057f46873Sjmatthew 
38157f46873Sjmatthew 	if (tls_accept_socket(l->tls, &conn->tls, conn->fd) < 0) {
38257f46873Sjmatthew 		log_debug("tls_accept_socket failed");
38357f46873Sjmatthew 		return -1;
38457f46873Sjmatthew 	}
38557f46873Sjmatthew 
38657f46873Sjmatthew 	conn->s_flags |= F_SECURE;
38757f46873Sjmatthew 	buffertls_set(&conn->buftls, conn->bev, conn->tls, conn->fd);
38857f46873Sjmatthew 	buffertls_accept(&conn->buftls, conn->fd);
38957f46873Sjmatthew 	return 0;
39057f46873Sjmatthew }
391