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