1/*	$OpenBSD: conn.c,v 1.21 2023/06/26 10:28:12 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
22#include <stdlib.h>
23#include <errno.h>
24#include <unistd.h>
25
26#include "ldapd.h"
27#include "log.h"
28
29int			 conn_dispatch(struct conn *conn);
30int			 conn_tls_init(struct conn *);
31unsigned int		 ldap_application(struct ber_element *elm);
32
33struct conn_list	 conn_list;
34
35unsigned int
36ldap_application(struct ber_element *elm)
37{
38	return BER_TYPE_OCTETSTRING;
39}
40
41void
42request_free(struct request *req)
43{
44	if (req->root != NULL)
45		ober_free_elements(req->root);
46	free(req);
47}
48
49void
50conn_close(struct conn *conn)
51{
52	struct search	*search, *next;
53	struct listener *l = conn->listener;
54
55	log_debug("closing connection %d", conn->fd);
56
57	/* Cancel any ongoing searches on this connection. */
58	for (search = TAILQ_FIRST(&conn->searches); search; search = next) {
59		next = TAILQ_NEXT(search, next);
60		search_close(search);
61	}
62
63	/* Cancel any queued requests on this connection. */
64	namespace_cancel_conn(conn);
65
66	tls_free(conn->tls);
67
68	TAILQ_REMOVE(&conn_list, conn, next);
69	ober_free(&conn->ber);
70	if (conn->bev != NULL)
71		bufferevent_free(conn->bev);
72	close(conn->fd);
73
74	/* Some file descriptors are available again. */
75	if (evtimer_pending(&l->evt, NULL)) {
76		evtimer_del(&l->evt);
77		event_add(&l->ev, NULL);
78	}
79
80	free(conn->binddn);
81	free(conn->pending_binddn);
82	free(conn);
83
84	--stats.conns;
85}
86
87/* Marks a connection for disconnect. The connection will be closed when
88 * any remaining data has been flushed to the socket.
89 */
90void
91conn_disconnect(struct conn *conn)
92{
93	conn->disconnect = 1;
94	bufferevent_enable(conn->bev, EV_WRITE);
95}
96
97void
98request_dispatch(struct request *req)
99{
100	unsigned long		 i;
101	struct {
102		unsigned int	 type;
103		int (*fn)(struct request *);
104	} requests[] = {
105		{ LDAP_REQ_SEARCH,	ldap_search },
106		{ LDAP_REQ_BIND,	ldap_bind },
107		{ LDAP_REQ_COMPARE,	ldap_compare },
108		{ LDAP_REQ_ADD,		ldap_add },
109		{ LDAP_REQ_UNBIND_30,	ldap_unbind },
110		{ LDAP_REQ_MODIFY,	ldap_modify },
111		{ LDAP_REQ_ABANDON_30,	ldap_abandon },
112		{ LDAP_REQ_DELETE_30,	ldap_delete },
113		{ LDAP_REQ_EXTENDED,	ldap_extended },
114		{ 0,			NULL }
115	};
116
117	/* RFC4511, section 4.2.1 says we shouldn't process other requests
118	 * while binding. A bind operation can, however, be aborted by sending
119	 * another bind operation.
120	 */
121	if (req->conn->bind_req != NULL && req->type != LDAP_REQ_BIND) {
122		log_warnx("got request while bind in progress");
123		ldap_respond(req, LDAP_SASL_BIND_IN_PROGRESS);
124		return;
125	}
126
127	for (i = 0; requests[i].fn != NULL; i++) {
128		if (requests[i].type == req->type) {
129			requests[i].fn(req);
130			break;
131		}
132	}
133
134	if (requests[i].fn == NULL) {
135		log_warnx("unhandled request %u (not implemented)", req->type);
136		ldap_respond(req, LDAP_PROTOCOL_ERROR);
137	}
138}
139
140int
141conn_dispatch(struct conn *conn)
142{
143	int			 class;
144	struct request		*req;
145	u_char			*rptr;
146
147	++stats.requests;
148
149	if ((req = calloc(1, sizeof(*req))) == NULL) {
150		log_warn("calloc");
151		conn_disconnect(conn);
152		return -1;
153	}
154
155	req->conn = conn;
156	rptr = conn->ber.br_rptr;	/* save where we start reading */
157
158	if ((req->root = ober_read_elements(&conn->ber, NULL)) == NULL) {
159		if (errno != ECANCELED) {
160			log_warnx("protocol error");
161			hexdump(rptr, conn->ber.br_rend - rptr,
162			    "failed to parse request from %zi bytes:",
163			    conn->ber.br_rend - rptr);
164			conn_disconnect(conn);
165		}
166		request_free(req);
167		return -1;
168	}
169	log_debug("consumed %ld bytes", conn->ber.br_rptr - rptr);
170
171	/* Read message id and request type.
172	 */
173	if (ober_scanf_elements(req->root, "{ite",
174	    &req->msgid, &class, &req->type, &req->op) != 0) {
175		log_warnx("protocol error");
176		ldap_debug_elements(req->root, -1,
177		    "received invalid request on fd %d", conn->fd);
178		conn_disconnect(conn);
179		request_free(req);
180		return -1;
181	}
182
183	ldap_debug_elements(req->root, req->type,
184	    "received request on fd %d", conn->fd);
185
186	log_debug("got request type %u, id %lld", req->type, req->msgid);
187	request_dispatch(req);
188	return 0;
189}
190
191void
192conn_read(struct bufferevent *bev, void *data)
193{
194	size_t			 nused = 0;
195	struct conn		*conn = data;
196	struct evbuffer		*input;
197
198	input = EVBUFFER_INPUT(bev);
199	ober_set_readbuf(&conn->ber,
200	    EVBUFFER_DATA(input), EVBUFFER_LENGTH(input));
201
202	while (conn->ber.br_rend - conn->ber.br_rptr > 0) {
203		if (conn_dispatch(conn) == 0)
204			nused = conn->ber.br_rptr - conn->ber.br_rbuf;
205		else
206			break;
207	}
208
209	evbuffer_drain(input, nused);
210}
211
212void
213conn_write(struct bufferevent *bev, void *data)
214{
215	struct search	*search, *next;
216	struct conn	*conn = data;
217
218	/* Continue any ongoing searches.
219	 * Note that the search may be unlinked and freed by conn_search.
220	 */
221	for (search = TAILQ_FIRST(&conn->searches); search; search = next) {
222		next = TAILQ_NEXT(search, next);
223		conn_search(search);
224	}
225
226	if (conn->disconnect)
227		conn_close(conn);
228	else if (conn->s_flags & F_STARTTLS) {
229		conn->s_flags &= ~F_STARTTLS;
230		if (conn_tls_init(conn) == -1)
231			conn_close(conn);
232	}
233}
234
235void
236conn_err(struct bufferevent *bev, short why, void *data)
237{
238	struct conn	*conn = data;
239
240	if ((why & EVBUFFER_EOF) == EVBUFFER_EOF)
241		log_debug("end-of-file on connection %d", conn->fd);
242	else if ((why & EVBUFFER_TIMEOUT) == EVBUFFER_TIMEOUT)
243		log_debug("timeout on connection %d", conn->fd);
244	else
245		log_warn("%s error on connection %d",
246		    why & EVBUFFER_WRITE ? "write" : "read", conn->fd);
247
248	conn_close(conn);
249}
250
251void
252conn_accept(int fd, short event, void *data)
253{
254	int			 afd;
255	socklen_t		 addrlen;
256	struct conn		*conn;
257	struct listener		*l = data;
258	struct sockaddr_storage	 remote_addr;
259	char			 host[128];
260
261	event_add(&l->ev, NULL);
262	if ((event & EV_TIMEOUT))
263		return;
264
265	addrlen = sizeof(remote_addr);
266	afd = accept_reserve(fd, (struct sockaddr *)&remote_addr, &addrlen,
267	    FD_RESERVE);
268	if (afd == -1) {
269		/*
270		 * Pause accept if we are out of file descriptors, or
271		 * libevent will haunt us here too.
272		 */
273		if (errno == ENFILE || errno == EMFILE) {
274			struct timeval evtpause = { 1, 0 };
275
276			event_del(&l->ev);
277			evtimer_add(&l->evt, &evtpause);
278		} else if (errno != EWOULDBLOCK && errno != EINTR)
279			log_warn("conn_accept");
280		return;
281	}
282
283	if (l->ss.ss_family == AF_UNIX) {
284		uid_t		 euid;
285		gid_t		 egid;
286
287		if (getpeereid(afd, &euid, &egid) == -1)
288			log_warnx("conn_accept: getpeereid");
289		else
290			log_debug("accepted local connection by uid %d", euid);
291	} else {
292		print_host(&remote_addr, host, sizeof(host));
293		log_debug("accepted connection from %s on fd %d", host, afd);
294	}
295
296	if ((conn = calloc(1, sizeof(*conn))) == NULL) {
297		log_warn("malloc");
298		goto giveup;
299	}
300	ober_set_application(&conn->ber, ldap_application);
301	conn->fd = afd;
302	conn->listener = l;
303
304	conn->bev = bufferevent_new(afd, conn_read, conn_write,
305	    conn_err, conn);
306	if (conn->bev == NULL) {
307		log_warn("conn_accept: bufferevent_new");
308		free(conn);
309		goto giveup;
310	}
311	bufferevent_enable(conn->bev, EV_READ);
312	bufferevent_settimeout(conn->bev, 0, 60);
313	if (l->flags & F_LDAPS)
314		if (conn_tls_init(conn) == -1) {
315			conn_close(conn);
316			goto giveup;
317		}
318
319	TAILQ_INIT(&conn->searches);
320	TAILQ_INSERT_HEAD(&conn_list, conn, next);
321
322	if (l->flags & F_SECURE)
323		conn->s_flags |= F_SECURE;
324
325	++stats.conns;
326	return;
327giveup:
328	close(afd);
329	/* Some file descriptors are available again. */
330	if (evtimer_pending(&l->evt, NULL)) {
331		evtimer_del(&l->evt);
332		event_add(&l->ev, NULL);
333	}
334}
335
336struct conn *
337conn_by_fd(int fd)
338{
339	struct conn		*conn;
340
341	TAILQ_FOREACH(conn, &conn_list, next) {
342		if (conn->fd == fd)
343			return conn;
344	}
345	return NULL;
346}
347
348int
349conn_close_any(void)
350{
351	struct conn		*conn;
352
353	/* Close oldest idle connection */
354	TAILQ_FOREACH_REVERSE(conn, &conn_list, conn_list, next) {
355		if (namespace_conn_queue_count(conn) == 0) {
356			conn_close(conn);
357			return 0;
358		}
359	}
360
361	/* Close oldest connection */
362	conn = TAILQ_LAST(&conn_list, conn_list);
363	if (conn != NULL) {
364		conn_close(conn);
365		return 0;
366	}
367
368	return -1;
369}
370
371int
372conn_tls_init(struct conn *conn)
373{
374	struct listener *l = conn->listener;
375
376	if (!(l->flags & F_SSL))
377		return 0;
378
379	log_debug("conn_tls_init: switching to TLS");
380
381	if (tls_accept_socket(l->tls, &conn->tls, conn->fd) < 0) {
382		log_debug("tls_accept_socket failed");
383		return -1;
384	}
385
386	conn->s_flags |= F_SECURE;
387	buffertls_set(&conn->buftls, conn->bev, conn->tls, conn->fd);
388	buffertls_accept(&conn->buftls, conn->fd);
389	return 0;
390}
391