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