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