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