xref: /openbsd/usr.sbin/ldapd/conn.c (revision 898184e3)
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