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