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