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