1 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "aqueue.h"
6 #include "ioloop.h"
7 #include "ldap-private.h"
8 
9 static
10 void ldap_connection_read_more(struct ldap_connection *conn);
11 static
12 int ldap_connect_next_message(struct ldap_connection *conn, struct ldap_op_queue_entry *req, bool *finished_r);
13 static
14 void ldap_connection_abort_request(struct ldap_op_queue_entry *req);
15 static
16 void ldap_connection_request_destroy(struct ldap_op_queue_entry **req);
17 static
18 int ldap_connection_connect(struct ldap_connection *conn);
19 static
20 void ldap_connection_send_next(struct ldap_connection *conn);
21 
ldap_connection_deinit(struct ldap_connection ** _conn)22 void ldap_connection_deinit(struct ldap_connection **_conn)
23 {
24 	struct ldap_connection *conn = *_conn;
25 
26 	*_conn = NULL;
27 
28 	ldap_connection_kill(conn);
29 
30 	unsigned int n = aqueue_count(conn->request_queue);
31 	for (unsigned int i = 0; i < n; i++) {
32 		struct ldap_op_queue_entry *req =
33 			array_idx_elem(&conn->request_array,
34 				       aqueue_idx(conn->request_queue, i));
35 		timeout_remove(&req->to_abort);
36 	}
37 	pool_unref(&conn->pool);
38 }
39 
40 static
ldap_connection_setup(struct ldap_connection * conn,const char ** error_r)41 int ldap_connection_setup(struct ldap_connection *conn, const char **error_r)
42 {
43 	int ret, opt;
44 
45 	ret = ldap_initialize(&conn->conn, conn->set.uri);
46 	if (ret != LDAP_SUCCESS) {
47 		*error_r = t_strdup_printf("ldap_initialize(uri=%s) failed: %s",
48 					   conn->set.uri, ldap_err2string(ret));
49 		return -1;
50 	}
51 
52 	if (conn->ssl_set.verify_remote_cert) {
53 		opt = LDAP_OPT_X_TLS_HARD;
54 	} else {
55 		opt = LDAP_OPT_X_TLS_ALLOW;
56 	}
57 
58 	ldap_set_option(conn->conn, LDAP_OPT_X_TLS, &opt);
59 	ldap_set_option(conn->conn, LDAP_OPT_X_TLS_REQUIRE_CERT, &opt);
60 #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
61 	/* refuse to connect to SSLv2 as it's completely insecure */
62 	opt = LDAP_OPT_X_TLS_PROTOCOL_SSL3;
63 	ldap_set_option(conn->conn, LDAP_OPT_X_TLS_PROTOCOL_MIN, &opt);
64 #endif
65 	opt = conn->set.timeout_secs;
66 	/* default timeout */
67 	ldap_set_option(conn->conn, LDAP_OPT_TIMEOUT, &opt);
68 	ldap_set_option(conn->conn, LDAP_OPT_NETWORK_TIMEOUT, &opt);
69 	/* timelimit */
70 	ldap_set_option(conn->conn, LDAP_OPT_TIMELIMIT, &opt);
71 
72 	if (conn->ssl_set.ca_file != NULL)
73 		ldap_set_option(conn->conn, LDAP_OPT_X_TLS_CACERTFILE, conn->ssl_set.ca_file);
74 	if (conn->ssl_set.ca_dir != NULL)
75 		ldap_set_option(conn->conn, LDAP_OPT_X_TLS_CACERTDIR, conn->ssl_set.ca_dir);
76 
77 	if (conn->ssl_set.cert.cert != NULL)
78 		ldap_set_option(conn->conn, LDAP_OPT_X_TLS_CERTFILE, conn->ssl_set.cert.cert);
79 	if (conn->ssl_set.cert.key != NULL)
80 		ldap_set_option(conn->conn, LDAP_OPT_X_TLS_KEYFILE, conn->ssl_set.cert.key);
81 
82 	opt = conn->set.debug;
83 	ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &opt);
84 
85 	opt = LDAP_VERSION3;
86 	ldap_set_option(conn->conn, LDAP_OPT_PROTOCOL_VERSION, &opt);
87 
88 	ldap_set_option(conn->conn, LDAP_OPT_REFERRALS, 0);
89 
90 #ifdef LDAP_OPT_X_TLS_NEWCTX
91 	opt = 0;
92 	ldap_set_option(conn->conn, LDAP_OPT_X_TLS_NEWCTX, &opt);
93 #endif
94 
95 	return 0;
96 }
97 
ldap_connection_have_settings(struct ldap_connection * conn,const struct ldap_client_settings * set)98 bool ldap_connection_have_settings(struct ldap_connection *conn,
99 				   const struct ldap_client_settings *set)
100 {
101 	const struct ldap_client_settings *conn_set = &conn->set;
102 
103 	if (strcmp(conn_set->uri, set->uri) != 0)
104 		return FALSE;
105 	if (null_strcmp(conn_set->bind_dn, set->bind_dn) != 0)
106 		return FALSE;
107 	if (null_strcmp(conn_set->password, set->password) != 0)
108 		return FALSE;
109 	if (conn_set->timeout_secs != set->timeout_secs ||
110 	    conn_set->max_idle_time_secs != set->max_idle_time_secs ||
111 	    conn_set->debug != set->debug ||
112 	    conn_set->require_ssl != set->require_ssl ||
113 	    conn_set->start_tls != set->start_tls)
114 		return FALSE;
115 
116 	if (set->ssl_set == NULL || !set->start_tls)
117 		return TRUE;
118 
119 	/* check SSL settings */
120 	if (null_strcmp(conn->ssl_set.min_protocol, set->ssl_set->min_protocol) != 0)
121 		return FALSE;
122 	if (null_strcmp(conn->ssl_set.cipher_list, set->ssl_set->cipher_list) != 0)
123 		return FALSE;
124 	if (null_strcmp(conn->ssl_set.ca_file, set->ssl_set->ca_file) != 0)
125 		return FALSE;
126 	if (null_strcmp(conn->ssl_set.cert.cert, set->ssl_set->cert.cert) != 0)
127 		return FALSE;
128 	if (null_strcmp(conn->ssl_set.cert.key, set->ssl_set->cert.key) != 0)
129 		return FALSE;
130 	return TRUE;
131 }
132 
ldap_connection_init(struct ldap_client * client,const struct ldap_client_settings * set,struct ldap_connection ** conn_r,const char ** error_r)133 int ldap_connection_init(struct ldap_client *client,
134 			 const struct ldap_client_settings *set,
135 			 struct ldap_connection **conn_r, const char **error_r)
136 {
137 	i_assert(set->uri != NULL);
138 
139 	if (set->require_ssl &&
140 	    !set->start_tls &&
141 	    strncmp("ldaps://",set->uri,8) != 0) {
142 		*error_r = t_strdup_printf("ldap_connection_init(uri=%s) failed: %s", set->uri,
143 			"uri does not start with ldaps and ssl required without start TLS");
144 		return -1;
145 	}
146 
147 	pool_t pool = pool_alloconly_create("ldap connection", 1024);
148 	struct ldap_connection *conn = p_new(pool, struct ldap_connection, 1);
149 	conn->pool = pool;
150 
151 	conn->client = client;
152 	conn->set = *set;
153 	/* deep copy relevant strings */
154 	conn->set.uri = p_strdup(pool, set->uri);
155 	conn->set.bind_dn = p_strdup(pool, set->bind_dn);
156 	if (set->password != NULL) {
157 		conn->set.password = p_strdup(pool, set->password);
158 		ber_str2bv(conn->set.password, strlen(conn->set.password), 0, &conn->cred);
159 	}
160 	/* cannot use these */
161 	conn->ssl_set.ca = NULL;
162 	conn->ssl_set.cert.key_password = NULL;
163 	conn->ssl_set.cert_username_field = NULL;
164 	conn->ssl_set.crypto_device = NULL;
165 
166 	if (set->ssl_set != NULL) {
167 		/* keep in sync with ldap_connection_have_settings() */
168 		conn->set.ssl_set = &conn->ssl_set;
169 		conn->ssl_set.min_protocol = p_strdup(pool, set->ssl_set->min_protocol);
170 		conn->ssl_set.cipher_list = p_strdup(pool, set->ssl_set->cipher_list);
171 		conn->ssl_set.ca_file = p_strdup(pool, set->ssl_set->ca_file);
172 		conn->ssl_set.cert.cert = p_strdup(pool, set->ssl_set->cert.cert);
173 		conn->ssl_set.cert.key = p_strdup(pool, set->ssl_set->cert.key);
174 	}
175 	i_assert(ldap_connection_have_settings(conn, set));
176 
177 	if (ldap_connection_setup(conn, error_r) < 0) {
178 		ldap_connection_deinit(&conn);
179 		return -1;
180 	}
181 
182 	p_array_init(&conn->request_array, conn->pool, 10);
183 	conn->request_queue = aqueue_init(&conn->request_array.arr);
184 
185 	*conn_r = conn;
186 	return 0;
187 }
188 
ldap_connection_switch_ioloop(struct ldap_connection * conn)189 void ldap_connection_switch_ioloop(struct ldap_connection *conn)
190 {
191 	if (conn->io != NULL)
192 		conn->io = io_loop_move_io(&conn->io);
193 	if (conn->to_disconnect != NULL)
194 		conn->to_disconnect = io_loop_move_timeout(&conn->to_disconnect);
195 	if (conn->to_reconnect != NULL)
196 		conn->to_reconnect = io_loop_move_timeout(&conn->to_reconnect);
197 	unsigned int n = aqueue_count(conn->request_queue);
198 
199 	for (unsigned int i = 0; i < n; i++) {
200 		struct ldap_op_queue_entry *req =
201 			array_idx_elem(&conn->request_array,
202 				       aqueue_idx(conn->request_queue, i));
203 		if (req->to_abort != NULL)
204 			req->to_abort = io_loop_move_timeout(&req->to_abort);
205 	}
206 }
207 
208 static void
ldap_connection_result_failure(struct ldap_connection * conn,struct ldap_op_queue_entry * req,int ret,const char * error)209 ldap_connection_result_failure(struct ldap_connection *conn,
210 			       struct ldap_op_queue_entry *req,
211 			       int ret, const char *error)
212 {
213 	struct ldap_result res;
214 	i_zero(&res);
215 	res.conn = conn;
216 	res.openldap_ret = ret;
217 	res.error_string = error;
218 	if (req->result_callback != NULL)
219 		req->result_callback(&res, req->result_callback_ctx);
220 	else
221 		i_error("%s", error);
222 	ldap_connection_kill(conn);
223 }
224 
225 static
ldap_connection_result_success(struct ldap_connection * conn,struct ldap_op_queue_entry * req)226 void ldap_connection_result_success(struct ldap_connection *conn,
227 				    struct ldap_op_queue_entry *req)
228 {
229 	struct ldap_result res;
230 	i_zero(&res);
231 	res.conn = conn;
232 	res.openldap_ret = LDAP_SUCCESS;
233 	if (req->result_callback != NULL)
234 		req->result_callback(&res, req->result_callback_ctx);
235 }
236 
237 static struct ldap_op_queue_entry *
ldap_connection_next_unsent_request(struct ldap_connection * conn,unsigned int * index_r)238 ldap_connection_next_unsent_request(struct ldap_connection *conn,
239 				    unsigned int *index_r)
240 {
241 	struct ldap_op_queue_entry *last_req = NULL;
242 	*index_r = 0;
243 
244 	for (unsigned int i = 0; i < aqueue_count(conn->request_queue); i++) {
245 		struct ldap_op_queue_entry *req =
246 			array_idx_elem(&conn->request_array,
247 				aqueue_idx(conn->request_queue, i));
248 		if (req->msgid > -1)
249 			break;
250 		*index_r = i;
251 		last_req = req;
252 	}
253 	return last_req;
254 }
255 
256 static
ldap_connection_send_next(struct ldap_connection * conn)257 void ldap_connection_send_next(struct ldap_connection *conn)
258 {
259 	unsigned int index;
260 	struct ldap_op_queue_entry *req;
261 
262 	timeout_remove(&conn->to_reconnect);
263 
264 	if (conn->state == LDAP_STATE_DISCONNECT) {
265 		if (ldap_connection_connect(conn) == -1)
266 			conn->to_reconnect = timeout_add(1000, ldap_connection_send_next, conn);
267 		return;
268 	}
269 
270 	if (conn->state != LDAP_STATE_CONNECT) {
271 		return;
272 	}
273 
274 	if (conn->pending > 10) return; /* try again later */
275 
276 	req = ldap_connection_next_unsent_request(conn, &index);
277 	/* nothing to actually send */
278 	if (req == NULL) return;
279 
280 	i_assert(req->msgid == -1);
281 
282 	const char *error;
283 	int ret;
284 	if ((ret = req->send_request_cb(conn, req, &error)) != LDAP_SUCCESS) {
285 		/* did not succeed */
286 		struct ldap_result res;
287 
288 		i_zero(&res);
289 		res.openldap_ret = ret;
290 		res.error_string = error;
291 		if (req->result_callback != NULL)
292 			req->result_callback(&res, req->result_callback_ctx);
293 
294 		ldap_connection_request_destroy(&req);
295 		aqueue_delete(conn->request_queue, index);
296 	} else conn->pending++;
297 }
298 
299 static
ldap_connection_request_destroy(struct ldap_op_queue_entry ** _req)300 void ldap_connection_request_destroy(struct ldap_op_queue_entry **_req)
301 {
302 	struct ldap_op_queue_entry *req = *_req;
303 
304 	*_req = NULL;
305 
306 	timeout_remove(&req->to_abort);
307 	pool_unref(&req->pool);
308 }
309 
ldap_connection_queue_request(struct ldap_connection * conn,struct ldap_op_queue_entry * req)310 void ldap_connection_queue_request(struct ldap_connection *conn, struct ldap_op_queue_entry *req)
311 {
312 	req->msgid = -1;
313 	req->conn = conn;
314 	aqueue_append(conn->request_queue, &req);
315 	if (req->timeout_secs > 0)
316 		req->to_abort = timeout_add(req->timeout_secs * 1000, ldap_connection_abort_request, req);
317 
318 	ldap_connection_send_next(conn);
319 }
320 
321 static int
ldap_connection_connect_parse(struct ldap_connection * conn,struct ldap_op_queue_entry * req,LDAPMessage * message,bool * finished_r)322 ldap_connection_connect_parse(struct ldap_connection *conn,
323 			      struct ldap_op_queue_entry *req,
324 			      LDAPMessage *message, bool *finished_r)
325 {
326 	int ret, result_err;
327 	char *retoid, *result_errmsg;
328 	int msgtype = ldap_msgtype(message);
329 
330 	*finished_r = TRUE;
331 	ret = ldap_parse_result(conn->conn, message, &result_err, NULL,
332 		&result_errmsg, NULL, NULL, 0);
333 
334 	switch(conn->state) {
335 	case LDAP_STATE_TLS:
336 		if (msgtype != LDAP_RES_EXTENDED) {
337 			*finished_r = FALSE;
338 			return LDAP_SUCCESS;
339 		}
340 		if (ret != 0) {
341 			ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
342 				"ldap_start_tls(uri=%s) failed: %s",
343 				conn->set.uri, ldap_err2string(ret)));
344 			return ret;
345 		} else if (result_err != 0) {
346 			if (conn->set.require_ssl) {
347 				ldap_connection_result_failure(conn, req, result_err, t_strdup_printf(
348 					"ldap_start_tls(uri=%s) failed: %s",
349 					conn->set.uri, result_errmsg));
350 				ldap_memfree(result_errmsg);
351 				return LDAP_INVALID_CREDENTIALS; /* make sure it disconnects */
352 			}
353 		} else {
354 			ret = ldap_parse_extended_result(conn->conn, message, &retoid, NULL, 0);
355 			/* retoid can be NULL even if ret == 0 */
356 			if (ret == 0) {
357 				ret = ldap_install_tls(conn->conn);
358 				if (ret != 0) {
359 					// if this fails we have to abort
360 					ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
361 						"ldap_start_tls(uri=%s) failed: %s",
362 						conn->set.uri, ldap_err2string(ret)));
363 					return LDAP_INVALID_CREDENTIALS;
364 				}
365 			}
366 			if (ret != LDAP_SUCCESS) {
367 				if (conn->set.require_ssl) {
368 					ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
369 						"ldap_start_tls(uri=%s) failed: %s",
370 						conn->set.uri, ldap_err2string(ret)));
371 					return LDAP_UNAVAILABLE;
372 				}
373 			} else {
374 				if (conn->set.debug > 0)
375 					i_debug("Using TLS connection to remote LDAP server");
376 			}
377 			ldap_memfree(retoid);
378 		}
379 		conn->state = LDAP_STATE_AUTH;
380 		return ldap_connect_next_message(conn, req, finished_r);
381 	case LDAP_STATE_AUTH:
382 		if (ret != LDAP_SUCCESS) {
383 			ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
384 				"ldap_parse_result() failed for connect: %s",
385 				ldap_err2string(ret)));
386 			return ret;
387 		}
388 		if (result_err != LDAP_SUCCESS) {
389 			const char *error = result_errmsg != NULL ?
390 				result_errmsg : ldap_err2string(result_err);
391 				ldap_connection_result_failure(conn, req, result_err, t_strdup_printf(
392 				"Connect failed: %s", error));
393 			ldap_memfree(result_errmsg);
394 			return result_err;
395 		}
396 		if (msgtype != LDAP_RES_BIND) return 0;
397 		ret = ldap_parse_sasl_bind_result(conn->conn, message, &conn->scred, 0);
398 		if (ret != LDAP_SUCCESS) {
399 			const char *error = t_strdup_printf(
400 				"Cannot bind with server: %s", ldap_err2string(ret));
401 			ldap_connection_result_failure(conn, req, ret, error);
402 			return 1;
403 		}
404 		conn->state = LDAP_STATE_CONNECT;
405 		return ldap_connect_next_message(conn, req, finished_r);
406 	default:
407 		i_unreached();
408 	}
409 	i_unreached();
410 }
411 
412 static
ldap_connection_abort_request(struct ldap_op_queue_entry * req)413 void ldap_connection_abort_request(struct ldap_op_queue_entry *req)
414 {
415 	struct ldap_result res;
416 
417 	/* too bad */
418 	timeout_remove(&req->to_abort);
419 	if (req->msgid > -1)
420 		ldap_abandon_ext(req->conn->conn, req->msgid, NULL, NULL);
421 
422 	i_zero(&res);
423 	res.openldap_ret = LDAP_TIMEOUT;
424 	res.error_string = "Aborting LDAP request after timeout";
425 	if (req->result_callback != NULL)
426 		req->result_callback(&res, req->result_callback_ctx);
427 
428 	unsigned int n = aqueue_count(req->conn->request_queue);
429 	for (unsigned int i = 0; i < n; i++) {
430 		struct ldap_op_queue_entry *arr_req =
431 			array_idx_elem(&req->conn->request_array,
432 				       aqueue_idx(req->conn->request_queue, i));
433 		if (req == arr_req) {
434 			aqueue_delete(req->conn->request_queue, i);
435 			ldap_connection_request_destroy(&req);
436 			return;
437 		}
438 	}
439 	i_unreached();
440 }
441 
442 static
ldap_connection_abort_all_requests(struct ldap_connection * conn)443 void ldap_connection_abort_all_requests(struct ldap_connection *conn)
444 {
445 	struct ldap_result res;
446 	i_zero(&res);
447 	res.openldap_ret = LDAP_TIMEOUT;
448 	res.error_string = "Aborting LDAP requests due to failure";
449 
450 	unsigned int n = aqueue_count(conn->request_queue);
451 	for (unsigned int i = 0; i < n; i++) {
452 		struct ldap_op_queue_entry **reqp =
453 			array_idx_modifiable(&conn->request_array,
454 		aqueue_idx(conn->request_queue, i));
455 		timeout_remove(&(*reqp)->to_abort);
456 		if ((*reqp)->result_callback != NULL)
457 			(*reqp)->result_callback(&res, (*reqp)->result_callback_ctx);
458 		ldap_connection_request_destroy(reqp);
459 	}
460 	aqueue_clear(conn->request_queue);
461 }
462 
463 static int
ldap_connect_next_message(struct ldap_connection * conn,struct ldap_op_queue_entry * req,bool * finished_r)464 ldap_connect_next_message(struct ldap_connection *conn,
465 			  struct ldap_op_queue_entry *req, bool *finished_r)
466 {
467 	int ret;
468 
469 	*finished_r = TRUE;
470 
471 	switch(conn->state) {
472 	case LDAP_STATE_DISCONNECT:
473 		/* if we should not disable SSL, and the URI is not ldaps:// */
474 		if (!conn->set.start_tls || strstr(conn->set.uri, "ldaps://") == NULL) {
475 			ret = ldap_start_tls(conn->conn, NULL, NULL, &req->msgid);
476 			if (ret != LDAP_SUCCESS) {
477 				ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
478 					"ldap_start_tls(uri=%s) failed: %s",
479 					conn->set.uri, ldap_err2string(ret)));
480 				return ret;
481 			}
482 			conn->state = LDAP_STATE_TLS;
483 			break;
484 		}
485 		conn->state = LDAP_STATE_AUTH;
486 		/* fall through */
487 	case LDAP_STATE_AUTH:
488 		ret = ldap_sasl_bind(conn->conn,
489 			conn->set.bind_dn,
490 			LDAP_SASL_SIMPLE,
491 			&conn->cred,
492 			NULL,
493 			NULL,
494 			&req->msgid);
495 		if (ret != LDAP_SUCCESS) {
496 			ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
497 				"ldap_sasl_bind(uri=%s, dn=%s) failed: %s",
498 				conn->set.uri, conn->set.bind_dn, ldap_err2string(ret)));
499 			return ret;
500 		}
501 		break;
502 	case LDAP_STATE_CONNECT:
503 		ldap_connection_result_success(conn, req);
504 		return LDAP_SUCCESS; /* we are done here */
505 	default:
506 		i_unreached();
507 	};
508 
509 	req->conn = conn;
510 	*finished_r = FALSE;
511 	return LDAP_SUCCESS;
512 }
513 
514 static
ldap_connection_connect(struct ldap_connection * conn)515 int ldap_connection_connect(struct ldap_connection *conn)
516 {
517 	const char *error;
518 	int fd;
519 	Sockbuf *sb;
520 	bool finished;
521 
522 	if (conn->conn == NULL) {
523 		/* try to reconnect after disconnection */
524 		if (ldap_connection_setup(conn, &error) < 0)
525 			i_error("%s", error);
526 	}
527 
528 	pool_t pool = pool_alloconly_create(MEMPOOL_GROWING "ldap bind", 128);
529 	struct ldap_op_queue_entry *req = p_new(pool, struct ldap_op_queue_entry, 1);
530 	req->pool = pool;
531 
532 	req->internal_response_cb = ldap_connection_connect_parse;
533 	req->timeout_secs = conn->set.timeout_secs;
534 
535 	if (ldap_connect_next_message(conn, req, &finished) != LDAP_SUCCESS ||
536 	    conn->conn == NULL) {
537 		pool_unref(&pool);
538 		return -1;
539 	}
540 	conn->pending++;
541 	aqueue_append(conn->request_queue, &req);
542 	/* start timeout */
543 	if (req->timeout_secs > 0)
544 		req->to_abort = timeout_add(req->timeout_secs * 1000, ldap_connection_abort_request, req);
545 
546 	ldap_get_option(conn->conn, LDAP_OPT_SOCKBUF, &sb);
547 	ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &fd);
548 	conn->io = io_add(fd, IO_READ, ldap_connection_read_more, conn);
549 	if (conn->set.max_idle_time_secs > 0)
550 		conn->to_disconnect = timeout_add(conn->set.max_idle_time_secs * 1000, ldap_connection_kill, conn);
551 	return 0;
552 }
553 
ldap_connection_kill(struct ldap_connection * conn)554 void ldap_connection_kill(struct ldap_connection *conn)
555 {
556 	io_remove_closed(&conn->io);
557 	timeout_remove(&conn->to_disconnect);
558 	timeout_remove(&conn->to_reconnect);
559 	if (conn->request_queue != NULL) {
560 		unsigned int n = aqueue_count(conn->request_queue);
561 
562 		for (unsigned int i = 0; i < n; i++) {
563 			struct ldap_op_queue_entry *req =
564 				array_idx_elem(&conn->request_array,
565 					       aqueue_idx(conn->request_queue, i));
566 			if (req->msgid > -1)
567 				ldap_abandon_ext(conn->conn, req->msgid, NULL, NULL);
568 			req->msgid = -1;
569 		}
570 	}
571 	if (conn->conn != NULL) {
572 		ldap_unbind_ext(conn->conn, NULL, NULL);
573 		ldap_memfree(conn->scred);
574 	}
575 	conn->conn = NULL;
576 	conn->state = LDAP_STATE_DISCONNECT;
577 }
578 
ldap_connection_check(struct ldap_connection * conn)579 int ldap_connection_check(struct ldap_connection *conn)
580 {
581 	/* it's not connected */
582 	if (conn->state == LDAP_STATE_DISCONNECT) return -1;
583 	return 0;
584 }
585 
586 static struct ldap_op_queue_entry *
ldap_connection_find_req_by_msgid(struct ldap_connection * conn,int msgid,unsigned int * idx_r)587 ldap_connection_find_req_by_msgid(struct ldap_connection *conn, int msgid,
588 				  unsigned int *idx_r)
589 {
590 	unsigned int i, n = aqueue_count(conn->request_queue);
591 	for (i = 0; i < n; i++) {
592 		struct ldap_op_queue_entry *req =
593 			array_idx_elem(&conn->request_array,
594 				       aqueue_idx(conn->request_queue, i));
595 		if (req->msgid == msgid) {
596 			*idx_r = i;
597 			return req;
598 		}
599 	}
600 	return NULL;
601 }
602 
603 static int
ldap_connection_handle_message(struct ldap_connection * conn,LDAPMessage * message)604 ldap_connection_handle_message(struct ldap_connection *conn,
605 			       LDAPMessage *message)
606 {
607 	struct ldap_op_queue_entry *req;
608 	unsigned int i = 0;
609 	bool finished = FALSE;
610 	int err = LDAP_SUCCESS;
611 
612 	/* we need to look at who it was for */
613 	req = ldap_connection_find_req_by_msgid(conn, ldap_msgid(message), &i);
614 	if (req != NULL)
615 		err = req->internal_response_cb(conn, req, message, &finished);
616 	ldap_msgfree(message);
617 
618 	switch(err) {
619 	case LDAP_SUCCESS:
620 		break;
621 	case LDAP_SERVER_DOWN:
622 #ifdef LDAP_CONNECT_ERROR
623 	case LDAP_CONNECT_ERROR:
624 #endif
625 	case LDAP_UNAVAILABLE:
626 	case LDAP_OPERATIONS_ERROR:
627 	case LDAP_BUSY:
628 		/* requeue */
629 		ldap_connection_kill(conn);
630 		ldap_connection_send_next(conn);
631 		finished = FALSE;
632 		break;
633 	case LDAP_INVALID_CREDENTIALS: {
634 		/* fail everything */
635 		ldap_connection_kill(conn);
636 		ldap_connection_abort_all_requests(conn);
637 		return 0;
638 	}
639 	case LDAP_SIZELIMIT_EXCEEDED:
640 	case LDAP_TIMELIMIT_EXCEEDED:
641 	case LDAP_NO_SUCH_ATTRIBUTE:
642 	case LDAP_UNDEFINED_TYPE:
643 	case LDAP_INAPPROPRIATE_MATCHING:
644 	case LDAP_CONSTRAINT_VIOLATION:
645 	case LDAP_TYPE_OR_VALUE_EXISTS:
646 	case LDAP_INVALID_SYNTAX:
647 	case LDAP_NO_SUCH_OBJECT:
648 	case LDAP_ALIAS_PROBLEM:
649 	case LDAP_INVALID_DN_SYNTAX:
650 	case LDAP_IS_LEAF:
651 	case LDAP_ALIAS_DEREF_PROBLEM:
652 	case LDAP_FILTER_ERROR:
653 	case LDAP_LOCAL_ERROR:
654 		finished = TRUE;
655 		break;
656 	default:
657 		/* ignore */
658 		break;
659 	}
660 
661 	if (finished) {
662 		i_assert(req != NULL);
663 		ldap_connection_request_destroy(&req);
664 		conn->pending--;
665 		aqueue_delete(conn->request_queue, i);
666 		return 1;
667 	}
668 	return 0;
669 }
670 
671 static
ldap_connection_read_more(struct ldap_connection * conn)672 void ldap_connection_read_more(struct ldap_connection *conn)
673 {
674 	struct timeval tv = {
675 		.tv_sec = 0,
676 		.tv_usec = 0
677 	};
678 
679 	LDAPMessage *message;
680 	int ret;
681 
682 	/* try get a message */
683 	ret = ldap_result(conn->conn, LDAP_RES_ANY, 0, &tv, &message);
684 	if (ret > 0)
685 		ret = ldap_connection_handle_message(conn, message);
686 
687 	if (ret == -1) {
688 		if (ldap_get_option(conn->conn, LDAP_OPT_RESULT_CODE, &ret) != LDAP_SUCCESS)
689 			i_unreached();
690 		if (ret != LDAP_SERVER_DOWN)
691 			i_error("ldap_result() failed: %s", ldap_err2string(ret));
692 		else
693 			i_error("Connection lost to LDAP server, reconnecting");
694 		/* kill me */
695 		ldap_connection_kill(conn);
696 	} else if (ret != 0) {
697 		ldap_connection_send_next(conn);
698 	}
699 	/* reset timeout */
700 	if (conn->to_disconnect != NULL)
701 		timeout_reset(conn->to_disconnect);
702 }
703 
ldap_result_has_failed(struct ldap_result * result)704 bool ldap_result_has_failed(struct ldap_result *result)
705 {
706 	i_assert((result->openldap_ret == LDAP_SUCCESS) == (result->error_string == NULL));
707 	return result->openldap_ret != LDAP_SUCCESS;
708 }
709 
ldap_result_get_error(struct ldap_result * result)710 const char *ldap_result_get_error(struct ldap_result *result)
711 {
712 	i_assert((result->openldap_ret == LDAP_SUCCESS) == (result->error_string == NULL));
713 	return result->error_string;
714 }
715