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