The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
connection.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 42018db7faf19e2db5cdd93df92c12f895e34166 $
19 * @file lib/ldap/connection.c
20 * @brief Asynchronous connection management functions for LDAP.
21 *
22 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 42018db7faf19e2db5cdd93df92c12f895e34166 $")
25
27
28#include <freeradius-devel/ldap/base.h>
29#include <freeradius-devel/util/debug.h>
30
31/*
32 * Lookup of libldap result message types to meaningful strings
33 */
34static char const *ldap_msg_types[UINT8_MAX + 1] = {
35 [LDAP_RES_BIND] = "bind response",
36 [LDAP_RES_SEARCH_ENTRY] = "search entry",
37 [LDAP_RES_SEARCH_REFERENCE] = "search reference",
38 [LDAP_RES_SEARCH_RESULT] = "search result",
39 [LDAP_RES_MODIFY] = "modify response",
40 [LDAP_RES_ADD] = "add response",
41 [LDAP_RES_DELETE] = "delete response",
42 [LDAP_RES_MODDN] = "modify dn response",
43 [LDAP_RES_COMPARE] = "compare response",
44 [LDAP_RES_EXTENDED] = "extended response",
45 [LDAP_RES_INTERMEDIATE] = "intermediate response"
46};
47
48
49/** Allocate and configure a new connection
50 *
51 * Configures both our ldap handle, and libldap's handle.
52 *
53 * This can be used by async code and async code as no attempt is made to connect
54 * to the LDAP server. An attempt will only be made if ldap_start_tls* or ldap_bind*
55 * functions are called.
56 *
57 * If called on an #fr_ldap_connection_t which has already been initialised, will
58 * clear any memory allocated to the connection, unbind the ldap handle, and reinitialise
59 * everything.
60 *
61 * @param[in] c to configure.
62 * @param[in] config to apply.
63 * @return
64 * - 0 on success.
65 * - -1 on error.
66 */
68{
69 LDAP *handle = NULL;
70 int ldap_errno, ldap_version, keepalive, probes, is_server;
71
72 fr_assert(config->server);
73
74 ldap_errno = ldap_initialize(&handle, config->server);
75 if (ldap_errno != LDAP_SUCCESS) {
76 ERROR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
77 error:
78 return -1;
79 }
80
81 DEBUG3("New connection %p libldap handle %p", c, handle);
82
83 c->config = config;
84 c->handle = handle;
85
86 /*
87 * We now have a connection structure, but no actual connection.
88 *
89 * Set a bunch of LDAP options, using common code.
90 */
91#define do_ldap_option(_option, _name, _value) \
92 if (ldap_set_option(c->handle, _option, _value) != LDAP_OPT_SUCCESS) do { \
93 ldap_get_option(c->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
94 ERROR("Failed setting connection option %s: %s", _name, \
95 (ldap_errno != LDAP_SUCCESS) ? ldap_err2string(ldap_errno) : "Unknown error"); \
96 goto error;\
97 } while (0)
98
99DIAG_OFF(unused-macros)
100#define maybe_ldap_option(_option, _name, _value) \
101 if (_value) do_ldap_option(_option, _name, _value)
102DIAG_ON(unused-macros)
103
104 /*
105 * Leave "dereference" unset to use the OpenLDAP default.
106 */
107 if (config->dereference_str) do_ldap_option(LDAP_OPT_DEREF, "dereference", &(config->dereference));
108
109 /*
110 * We handle our own referral chasing as there is no way to
111 * get the fd for a referred query.
112 */
113 do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_OFF);
114
115 /*
116 * A value of zero results in an handle configuration failure.
117 *
118 * When most people specify zero they mean infinite.
119 *
120 * libldap requires tv_sec to be -1 to mean that.
121 */
122 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
123 (fr_time_delta_ispos(config->net_timeout) ?
124 &fr_time_delta_to_timeval(config->net_timeout) :
125 &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
126
127 do_ldap_option(LDAP_OPT_TIMELIMIT, "srv_timelimit", &fr_time_delta_to_timeval(config->srv_timelimit));
128
129 ldap_version = LDAP_VERSION3;
130 do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version);
131
132 keepalive = fr_time_delta_to_sec(config->keepalive_idle);
133 do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive_idle", &keepalive);
134
135 probes = config->keepalive_probes;
136 do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive_probes", &probes);
137
138 keepalive = fr_time_delta_to_sec(config->keepalive_interval);
139 do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive_interval", &keepalive);
140
141 if (config->sasl_secprops) do_ldap_option(LDAP_OPT_X_SASL_SECPROPS, "sasl_secprops", config->sasl_secprops);
142
143 /*
144 * Everything after this point is TLS related - so don't set if TLS not in use.
145 */
146 if (!config->tls_mode && !config->start_tls) return 0;
147
148 /*
149 * Set all of the TLS options
150 */
151 if (config->tls_mode) do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(config->tls_mode));
152
153 maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "ca_file", config->tls_ca_file);
154 maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "ca_path", config->tls_ca_path);
155
156 /*
157 * Set certificate options
158 */
159 maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certificate_file", config->tls_certificate_file);
160 maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "private_key_file", config->tls_private_key_file);
161
162 if (config->tls_require_cert_str) {
163 do_ldap_option(LDAP_OPT_X_TLS_REQUIRE_CERT, "require_cert", &config->tls_require_cert);
164 }
165
166 if (config->tls_min_version_str) {
167 do_ldap_option(LDAP_OPT_X_TLS_PROTOCOL_MIN, "tls_min_version", &config->tls_min_version);
168 }
169
170 /*
171 * Counter intuitively the TLS context appears to need to be initialised
172 * after all the TLS options are set on the handle.
173 */
174
175 /* Always use the new TLS configuration context */
176 is_server = 0;
177 do_ldap_option(LDAP_OPT_X_TLS_NEWCTX, "new TLS context", &is_server);
178
179 if (config->start_tls) {
180 if (config->port == LDAPS_PORT) {
181 WARN("Told to Start TLS on LDAPS port this will probably fail, please correct the "
182 "configuration");
183 }
184 }
185
186 return 0;
187}
188
189/** Free the handle, closing the connection to ldap
190 *
191 * @param[in] el UNUSED.
192 * @param[in] h to close.
193 * @param[in] uctx Connection config and handle.
194 */
196{
197 fr_ldap_connection_t *c = talloc_get_type_abort(h, fr_ldap_connection_t);
198
199 /*
200 * Explicitly remove the file descriptor event
201 *
202 * Even if the fr_ldap_connection_t has outstanding
203 * queries, we still don't want its fd in the event loop.
204 */
205 if (c->fd >= 0) {
207 c->fd = -1;
208 }
209
210 talloc_free(h);
211}
212
213/** Close and delete a connection
214 *
215 * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
216 * connection handle.
217 *
218 * @param[in] c to destroy.
219 * @return always indicates success.
220 */
222{
223 /*
224 * If there are any pending queries, don't free
225 */
226 if (((c->queries) && (fr_rb_num_elements(c->queries) > 0)) || (fr_dlist_num_elements(&c->refs) > 0)) return -1;
227
228 talloc_free_children(c); /* Force inverted free order */
229
230 if (c->handle) {
231 LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
232 LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
233
234 fr_ldap_control_merge(our_serverctrls, our_clientctrls,
235 NUM_ELEMENTS(our_serverctrls),
236 NUM_ELEMENTS(our_clientctrls),
237 c, NULL, NULL);
238
239 DEBUG3("Closing connection %p libldap handle %p", c, c->handle);
240 ldap_unbind_ext(c->handle, our_serverctrls, our_clientctrls); /* Same code as ldap_unbind_ext_s */
241 }
242
244
245 return 0;
246}
247
248/** Allocate our ldap connection handle layer
249 *
250 * This is using handles outside of the connection state machine.
251 *
252 * @param[in] ctx to allocate connection handle in.
253 * @return
254 * - A new unbound/unconfigured connection handle on success.
255 * Call f#r_ldap_connection_configure next.
256 * - NULL on OOM.
257 */
259{
261
262 /*
263 * Allocate memory for the handle.
264 */
265 c = talloc_zero(ctx, fr_ldap_connection_t);
266 if (!c) return NULL;
267
268 talloc_set_destructor(c, _ldap_connection_free);
269
270 /*
271 * Ensure the fd is invalid to start with, preventing
272 * attempts to remove fd events if the server is shut down
273 * before the LDAP connection is established
274 */
275 c->fd = -1;
276
277 return c;
278}
279
280/** Watcher for LDAP connections being closed
281 *
282 * If there are any outstanding queries on the connection then
283 * re-parent the connection to the NULL ctx so that it remains
284 * until all the queries have been dealt with.
285 */
287 UNUSED connection_state_t state, void *uctx)
288{
289 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(uctx, fr_ldap_connection_t);
290
291 if ((fr_rb_num_elements(ldap_conn->queries) == 0) && (fr_dlist_num_elements(&ldap_conn->refs) == 0)) return;
292
293 talloc_reparent(conn, NULL, ldap_conn);
294 ldap_conn->conn = NULL;
295}
296
297/** (Re-)Initialises the libldap side of the connection handle
298 *
299 * The first ldap state transition is either:
300 *
301 * init -> start tls
302 * or
303 * init -> bind
304 *
305 * Either way libldap will try an open the connection so when fr_ldap_state_next
306 * returns we should have the file descriptor to pass back.
307 *
308 * The complete order of operations is:
309 *
310 * - Initialise the libldap handle with fr_ldap_connection_configure (calls ldap_init)
311 * - Initiate the connection with fr_ldap_state_next, which either binds or calls start_tls.
312 * - Either operation calls ldap_send_server_request.
313 * - Which calls ldap_new_connection.
314 * - Which calls ldap_int_open_connection.
315 * - Which calls ldap_connect_to_(host|path) and adds socket buffers, and possibly
316 * calls ldap_int_tls_start (for ldaps://).
317 * - When ldap_new_connection returns, because LDAP_OPT_CONNECT_ASYNC
318 * is set to LDAP_OPT_ON, lc->lconn_status is set to LDAP_CONNST_CONNECTING.
319 * - ldap_send_server_request checks for lconn_stats == LDAP_CONNST_CONNECTING,
320 * and calls ldap_int_poll, which checks the fd for error conditions
321 * and immediately returns due to the network timeout value.
322 * - If the socket is not yet connected:
323 * - As network timeout on the LDAP handle is 0, ld->ld_errno is set to
324 * LDAP_X_CONNECTING. ldap_send_server_request returns -1.
325 * - bind or start_tls errors with LDAP_X_CONNECTING without sending the request.
326 * - We install a write I/O handler, and wait to be called again, then we retry the
327 * operation.
328 * - else
329 * - the bind or start_tls operation succeeds, our ldap state machine advances,
330 * the connection callback is called and our socket state machine transitions to
331 * connected.
332 * - Continue running the state machine
333 *
334 * @param[out] h Underlying file descriptor from libldap handle.
335 * @param[in] conn Being initialised.
336 * @param[in] config LDAP connection configuration.
337 * @param[in] directory Where the properties of the directory being connected to are recorded.
338 * May be NULL if directory discovery is not being performed.
339 * @return
340 * - CONNECTION_STATE_CONNECTING on success.
341 * - CONNECTION_STATE_FAILED on failure.
342 */
345{
347 fr_ldap_state_t state;
348
349 c = fr_ldap_connection_alloc(conn);
350 c->conn = conn;
351 c->directory = directory;
352 /*
353 * Initialise tree for outstanding queries handled by this connection
354 */
357
358 /*
359 * Configure/allocate the libldap handle
360 */
362 error:
363 talloc_free(c);
365 }
366
367 /* Don't block */
368 if (ldap_set_option(c->handle, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON) != LDAP_OPT_SUCCESS) goto error;
369 fr_ldap_connection_timeout_set(c, fr_time_delta_wrap(0)); /* Forces LDAP_X_CONNECTING */
370
371 state = fr_ldap_state_next(c);
372 if (state == FR_LDAP_STATE_ERROR) goto error;
373
375
376 *h = c; /* Set the handle */
377
379}
380
381/** Initialise a standalone LDAP connection
382 *
383 * @param[out] h Underlying file descriptor from libldap handle.
384 * @param[in] conn Being initialised.
385 * @param[in] uctx Connection configuration (a #fr_ldap_config_t).
386 * @return
387 * - CONNECTION_STATE_CONNECTING on success.
388 * - CONNECTION_STATE_FAILED on failure.
389 */
390CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
392{
393 fr_ldap_config_t const *config = uctx;
394
395 return ldap_connection_init(h, conn, config, NULL);
396}
397
398/** Initialise an LDAP trunk connection
399 *
400 * @param[out] h Underlying file descriptor from libldap handle.
401 * @param[in] conn Being initialised.
402 * @param[in] uctx Trunk the connection belongs to (a #fr_ldap_thread_trunk_t).
403 * @return
404 * - CONNECTION_STATE_CONNECTING on success.
405 * - CONNECTION_STATE_FAILED on failure.
406 */
407CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
409{
410 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
411
412 return ldap_connection_init(h, conn, &ttrunk->config, ttrunk->directory);
413}
414
416{
417 int ldap_errno;
418
419 /*
420 * A value of zero results in an handle configuration failure.
421 *
422 * When most people specify zero they mean infinite.
423 *
424 * libldap requires tv_sec to be -1 to mean that.
425 */
426 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
427 (fr_time_delta_ispos(timeout) ?
428 &fr_time_delta_to_timeval(timeout) :
429 &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
430
431 return 0;
432
433error:
434 return -1;
435}
436
438{
439 int ldap_errno;
440
441 /*
442 * A value of zero results in an handle configuration failure.
443 *
444 * When most people specify zero they mean infinite.
445 *
446 * libldap requires tv_sec to be -1 to mean that.
447 */
448 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
451 &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
452
453 return 0;
454
455error:
456 return -1;
457}
458
459/** Callback for closing idle LDAP trunk
460 *
461 */
463{
464 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
465
466 if (ttrunk->trunk->req_alloc == 0) {
467 DEBUG2("Removing idle LDAP trunk to \"%s\"", ttrunk->uri);
468 talloc_free(ttrunk->trunk);
469 talloc_free(ttrunk);
470 } else {
471 /*
472 * There are still pending queries - insert a new event
473 */
474 (void) fr_timer_in(ttrunk, tl, &ttrunk->ev, ttrunk->t->config->idle_timeout,
475 false, _ldap_trunk_idle_timeout, ttrunk);
476 }
477}
478
479/** Callback when an LDAP trunk request is cancelled
480 *
481 * Ensure the request is removed from the list of outstanding requests
482 */
484 UNUSED void *uctx) {
485 fr_ldap_query_t *query = talloc_get_type_abort(preq, fr_ldap_query_t);
486
487 if (query->ldap_conn) {
488 fr_rb_remove(query->ldap_conn->queries, query);
489 query->ldap_conn = NULL;
490 }
491}
492
493/** Callback to cancel LDAP queries
494 *
495 * Inform the remote LDAP server that we no longer want responses to specific queries.
496 *
497 * @param[in] el For timer management.
498 * @param[in] tconn The trunk connection handle
499 * @param[in] conn The specific connection queries will be cancelled on
500 * @param[in] uctx Context provided to trunk_alloc
501 */
502CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
504 connection_t *conn, UNUSED void *uctx)
505{
506 trunk_request_t *treq;
507 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
508 fr_ldap_query_t *query;
509
510 while ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
511 query = talloc_get_type_abort(treq->preq, fr_ldap_query_t);
512 ldap_abandon_ext(ldap_conn->handle, query->msgid, NULL, NULL);
513
515 }
516}
517
518/** Callback to tidy up when a trunk request fails
519 *
520 */
521static void ldap_request_fail(request_t *request, void *preq, UNUSED void *rctx,
522 UNUSED trunk_request_state_t state, UNUSED void *uctx)
523{
524 fr_ldap_query_t *query = talloc_get_type_abort(preq, fr_ldap_query_t);
525
526 /*
527 * Failed trunk requests get freed - so remove association in query.
528 */
529 query->treq = NULL;
530 query->ret = LDAP_RESULT_ERROR;
531
532 /*
533 * Ensure request is runnable.
534 */
535 if (request) unlang_interpret_mark_runnable(request);
536}
537
538TRUNK_NOTIFY_FUNC(ldap_trunk_connection_notify, fr_ldap_connection_t)
539
540/** Allocate an LDAP trunk connection
541 *
542 * @param[in] tconn Trunk handle.
543 * @param[in] el Event list which will be used for I/O and timer events.
544 * @param[in] conn_conf Configuration of the connection.
545 * @param[in] log_prefix What to prefix log messages with.
546 * @param[in] uctx User context passed to trunk_alloc.
547 */
548CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
550 UNUSED connection_conf_t const *conn_conf,
551 char const *log_prefix, void *uctx)
552{
553 fr_ldap_thread_trunk_t *thread_trunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
554 connection_t *conn;
555
556 conn = connection_alloc(tconn, el,
560 },
562 .connection_timeout = thread_trunk->config.net_timeout,
563 .reconnection_delay = thread_trunk->config.reconnection_delay
564 },
565 log_prefix, thread_trunk);
566 if (!conn) {
567 PERROR("Failed allocating state handler for new LDAP trunk connection");
568 return NULL;
569 }
570
571 return conn;
572}
573
574#define POPULATE_LDAP_CONTROLS(_dest, _src) do { \
575 int i; \
576 for (i = 0; (i < LDAP_MAX_CONTROLS) && (_src[i].control); i++) { \
577 _dest[i] = _src[i].control; \
578 } \
579 _dest[i] = NULL; \
580} while (0)
581
582/** Take LDAP pending queries from the queue and send them.
583 *
584 * @param[in] el Event list for timers.
585 * @param[in] tconn Trunk handle.
586 * @param[in] conn on which to send the queries
587 * @param[in] uctx User context passed to trunk_alloc
588 */
589CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
591 connection_t *conn, UNUSED void *uctx)
592{
593 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
594 trunk_request_t *treq;
595
596 LDAPURLDesc *referral_url = NULL;
597
598 fr_ldap_query_t *query = NULL;
599 fr_ldap_rcode_t status;
600
601 while (trunk_connection_pop_request(&treq, tconn) == 0) {
602 LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS + 1];
603 LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS + 1];
604
605 if (!treq) break;
606
607 query = talloc_get_type_abort(treq->preq, fr_ldap_query_t);
608
609 switch (query->type) {
611 /*
612 * This query is a LDAP search
613 */
614 if (query->referral) referral_url = query->referral->referral_url;
615
616 POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
617 POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
618
619 /*
620 * If we are chasing a referral, referral_url will be populated and may
621 * have a base dn or scope to override the original query
622 */
623 status = fr_ldap_search_async(&query->msgid, query->treq->request, ldap_conn,
624 (referral_url && referral_url->lud_dn) ?
625 referral_url->lud_dn : query->dn,
626 (referral_url && referral_url->lud_scope) ?
627 referral_url->lud_scope : query->search.scope,
628 query->search.filter, query->search.attrs,
629 our_serverctrls, our_clientctrls);
630 break;
631
633 /*
634 * Send a request to modify an object
635 */
636 POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
637 POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
638
639 status = fr_ldap_modify_async(&query->msgid, query->treq->request,
640 ldap_conn, query->dn, query->mods,
641 our_serverctrls, our_clientctrls);
642 break;
643
645 /*
646 * Send a request to delete an object
647 */
648 POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
649 POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
650
651 status = fr_ldap_delete_async(&query->msgid, query->treq->request,
652 ldap_conn, query->dn,
653 our_serverctrls, our_clientctrls);
654 break;
655
657 /*
658 * This query is an LDAP extended operation.
659 */
660 status = fr_ldap_extended_async(&query->msgid, query->treq->request, ldap_conn,
661 query->extended.reqoid, query->extended.reqdata);
662 break;
663
664 default:
665 status = LDAP_PROC_ERROR;
666 ERROR("Invalid LDAP query for trunk connection");
667 error:
670 continue;
671
672 }
673
674 if (status != LDAP_PROC_SUCCESS) goto error;
675
676 /*
677 * If the query has previously been associated with a different
678 * connection, remove that reference. Typically when following references.
679 */
680 if (query->ldap_conn) fr_dlist_remove(&query->ldap_conn->refs, query);
681
682 /*
683 * Record which connection was used for this query
684 * - results processing often needs access to an LDAP handle
685 */
686 query->ldap_conn = ldap_conn;
687
688 /*
689 * Add the query to the tree of pending queries for this trunk
690 */
691 fr_rb_insert(query->ldap_conn->queries, query);
692
694 }
695
696}
697
698/** Read LDAP responses
699 *
700 * Responses from the LDAP server will cause the fd to become readable and trigger this
701 * callback. Most LDAP search responses have multiple messages in their response - we
702 * only gather those which are complete before either following a referral or passing
703 * the head of the resulting chain of messages back.
704 *
705 * @param[in] el To insert timers into.
706 * @param[in] tconn Trunk connection associated with these results.
707 * @param[in] conn Connection handle for these results.
708 * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
709 */
710CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
712{
713 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
714 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
715
716 int ret = 0, msgtype;
717 struct timeval poll = { 0, 10 };
718 LDAPMessage *result = NULL;
719 fr_ldap_rcode_t rcode;
720 fr_ldap_query_t find = { .msgid = -1 }, *query = NULL;
721 request_t *request;
722 bool really_no_result = false;
723 trunk_request_t *treq;
724
725 /*
726 * Reset the idle timeout event
727 */
728 (void) fr_timer_in(ttrunk, el->tl, &ttrunk->ev,
729 ttrunk->t->config->idle_timeout, false, _ldap_trunk_idle_timeout, ttrunk);
730
731 do {
732 /*
733 * Look for any results for which we have the complete result message
734 * ldap_result will return a pointer to a chain of messages.
735 *
736 * The first time ldap_result is called when there's pending network
737 * data, it may read the data, but not return any results.
738 *
739 * In order to fix the spurious debugging messages and overhead,
740 * if this is the first iteration through the loop and ldap_result
741 * returns no result (0), we call it again.
742 */
743 ret = ldap_result(ldap_conn->handle, LDAP_RES_ANY, LDAP_MSG_ALL, &poll, &result);
744 switch (ret) {
745 case 0:
746 if (really_no_result) return;
747 really_no_result = true;
748 continue;
749
750 case -1:
751 rcode = fr_ldap_error_check(NULL, ldap_conn, NULL, NULL);
752 if (rcode == LDAP_PROC_BAD_CONN) {
753 ERROR("Bad LDAP connection");
755 }
756 return;
757
758 default:
759 /*
760 * We only retry ldap_result the first time through the loop.
761 */
762 really_no_result = true;
763 break;
764 }
765
766 find.msgid = ldap_msgid(result);
767 query = fr_rb_find(ldap_conn->queries, &find);
768
769 if (!query) {
770 WARN("Ignoring msgid %i - doesn't match any outstanding queries (it may have been cancelled)",
771 find.msgid);
772 ldap_msgfree(result);
773 continue;
774 }
775
776 /*
777 * Remove the query from the tree of outstanding queries
778 */
779 fr_rb_remove(ldap_conn->queries, query);
780
781 /*
782 * Add the query to the list of queries referencing this connection.
783 * Prevents the connection from being freed until the query has finished using it.
784 */
785 fr_dlist_insert_tail(&ldap_conn->refs, query);
786
787 /*
788 * This really shouldn't happen - as we only retrieve complete sets of results -
789 * but as the query data structure will last until its results are fully handled
790 * better to have this safety check here.
791 */
792 if (query->ret != LDAP_RESULT_PENDING) {
793 WARN("Received results for msgid %i which has already been handled - ignoring", find.msgid);
794 ldap_msgfree(result);
795 continue;
796 }
797
798 msgtype = ldap_msgtype(result);
799
800 /*
801 * Request to reference in debug output
802 */
803 request = query->treq->request;
804
805 ROPTIONAL(RDEBUG2, DEBUG2, "Got %s response for message %d",
806 ((unsigned int)msgtype < NUM_ELEMENTS(ldap_msg_types) && ldap_msg_types[msgtype]) ?
807 ldap_msg_types[msgtype] : "unknown", query->msgid);
808 rcode = fr_ldap_error_check(NULL, ldap_conn, result, query->dn);
809
810 switch (rcode) {
812 switch (query->type) {
814 query->ret = (ldap_count_entries(ldap_conn->handle, result) == 0) ?
816 break;
817
818 default:
819 query->ret = LDAP_RESULT_SUCCESS;
820 break;
821 }
822 break;
823
825 if (!ttrunk->t->config->chase_referrals) {
827 "LDAP referral received but 'chase_referrals' is set to 'no'");
828 query->ret = LDAP_RESULT_EXCESS_REFERRALS;
829 break;
830 }
831
832 if (query->referral_depth >= ttrunk->t->config->referral_depth) {
833 ROPTIONAL(REDEBUG, ERROR, "Maximum LDAP referral depth (%d) exceeded",
834 ttrunk->t->config->referral_depth);
835 query->ret = LDAP_RESULT_EXCESS_REFERRALS;
836 break;
837 }
838
839 /*
840 * If we've come here as the result of an existing referral
841 * clear the previous list of URLs before getting the next list.
842 */
843 if (query->referral_urls) ldap_memvfree((void **)query->referral_urls);
844
845 ldap_get_option(ldap_conn->handle, LDAP_OPT_REFERRAL_URLS, &query->referral_urls);
846 if (!(query->referral_urls) || (!(query->referral_urls[0]))) {
847 ROPTIONAL(REDEBUG, ERROR, "LDAP referral missing referral URL");
848 query->ret = LDAP_RESULT_MISSING_REFERRAL;
849 break;
850 }
851
852 query->referral_depth ++;
853
854 if (fr_ldap_referral_follow(ttrunk->t, request, query) == 0) {
855 next_follow:
856 ldap_msgfree(result);
857 continue;
858 }
859
860 ROPTIONAL(REDEBUG, ERROR, "Unable to follow any LDAP referral URLs");
861 query->ret = LDAP_RESULT_REFERRAL_FAIL;
862 break;
863
864 case LDAP_PROC_BAD_DN:
865 ROPTIONAL(RDEBUG2, DEBUG2, "DN %s does not exist", query->dn);
866 query->ret = LDAP_RESULT_BAD_DN;
867 break;
868
869 default:
870 ROPTIONAL(RPERROR, PERROR, "LDAP server returned an error");
871
872 if (query->referral_depth > 0) {
873 /*
874 * We're processing a referral - see if there are any more to try
875 */
876 fr_dlist_talloc_free_item(&query->referrals, query->referral);
877 query->referral = NULL;
878
879 if ((fr_dlist_num_elements(&query->referrals) > 0) &&
880 (fr_ldap_referral_next(ttrunk->t, request, query) == 0)) goto next_follow;
881 }
882
883 query->ret = LDAP_RESULT_REFERRAL_FAIL;
884 break;
885 }
886
887 /*
888 * Remove the timeout event
889 */
890 FR_TIMER_DELETE(&query->ev);
891 query->result = result;
892
893 /*
894 * If we have a specific parser to handle the result, call it
895 */
896 if (query->parser && (rcode == LDAP_PROC_SUCCESS)) query->parser(ldap_conn->handle, query,
897 result, query->treq->rctx);
898
899 /*
900 * Set the request as runnable
901 */
902 if (request) unlang_interpret_mark_runnable(request);
903
904 /*
905 * If referral following failed, there is no active trunk request.
906 */
907 if (!query->treq) continue;
908
909 /*
910 * If the query is parented off the treq then it will be freed when
911 * the request is completed. If it is parented by something else then it will not.
912 */
913 treq = query->treq;
914 query->treq = NULL;
916 } while (1);
917}
918
920{
921 if (ttrunk->t && fr_rb_node_inline_in_tree(&ttrunk->node)) fr_rb_remove(ttrunk->t->trunks, ttrunk);
922
923 return 0;
924}
925
926/** Find a thread specific LDAP connection for a specific URI / bind DN
927 *
928 * If no existing connection exists for that combination then create a new one
929 *
930 * @param[in] thread to which the connection belongs
931 * @param[in] uri of the host to find / create a connection to
932 * @param[in] bind_dn to make the connection as
933 * @param[in] bind_password for making connection
934 * @param[in] request currently being processed (only for debug messages)
935 * @param[in] config LDAP config of the module requesting the connection.
936 * @return
937 * - an existing or new connection matching the URI and bind DN
938 * - NULL on failure
939 */
941 char const *bind_dn, char const *bind_password,
942 request_t *request, fr_ldap_config_t const *config)
943{
944 fr_ldap_thread_trunk_t *found, find = {.uri = uri, .bind_dn = bind_dn};
946 trunk_conf_t trunk_conf;
947
948 ROPTIONAL(RDEBUG2, DEBUG2, "Looking for LDAP connection to \"%s\" bound as \"%s\"", uri,
949 bind_dn ? bind_dn : "(anonymous)");
950 found = fr_rb_find(thread->trunks, &find);
951
952 if (found) return found;
953
954 /*
955 * No existing connection matching the requirement - create a new one
956 */
957 ROPTIONAL(RDEBUG2, DEBUG2, "No existing connection new - creating new one");
958 MEM(new = talloc_zero(thread, fr_ldap_thread_trunk_t));
959 talloc_set_destructor(new, _thread_ldap_trunk_free);
960
961 /*
962 * Build config for this connection - start with module settings and
963 * override server and bind details
964 */
965 memcpy(&new->config, config, sizeof(fr_ldap_config_t));
966 new->config.server = talloc_strdup(new, uri);
967 new->config.admin_identity = talloc_strdup(new, bind_dn);
968 new->config.admin_password = talloc_strdup(new, bind_password);
969
970 new->uri = new->config.server;
971 new->bind_dn = new->config.admin_identity;
972
973 /*
974 * Allocated before the trunk so the pointer is valid when
975 * connection init callbacks run, populated by the first
976 * connection's discovery state before it starts serving
977 * requests.
978 */
979 new->directory = fr_ldap_directory_alloc(new);
980
981 /*
982 * Referral chasing creates trunks and waits for one to
983 * transition to active before sending the query, so a trunk
984 * must always open a connection, even with no requests
985 * enqueued.
986 */
987 trunk_conf = *thread->trunk_conf;
988 if (trunk_conf.start == 0) trunk_conf.start = 1;
989
990 new->trunk = trunk_alloc(new, thread->el,
992 .connection_alloc = ldap_trunk_connection_alloc,
993 .connection_notify = ldap_trunk_connection_notify,
994 .request_mux = ldap_trunk_request_mux,
995 .request_demux = ldap_trunk_request_demux,
996 .request_cancel = ldap_request_cancel,
997 .request_cancel_mux = ldap_request_cancel_mux,
998 .request_fail = ldap_request_fail,
999 },
1000 &trunk_conf,
1001 "rlm_ldap", new, false, thread->trigger_args);
1002
1003 if (!new->trunk) {
1004 error:
1005 ROPTIONAL(REDEBUG, ERROR, "Unable to create LDAP connection");
1006 talloc_free(new);
1007 return NULL;
1008 }
1009
1010 new->t = thread;
1011
1012 /*
1013 * Insert event to close trunk if it becomes idle
1014 */
1015 if (!fr_cond_assert_msg(fr_timer_in(new, thread->el->tl, &new->ev, thread->config->idle_timeout,
1016 false, _ldap_trunk_idle_timeout, new) == 0, "cannot insert trunk idle event")) goto error;
1017
1018 fr_rb_insert(thread->trunks, new);
1019
1020 return new;
1021}
1022
1023/** Lookup the state of a thread specific LDAP connection trunk for a specific URI / bind DN
1024 *
1025 * @param[in] thread to which the connection belongs
1026 * @param[in] uri of the host to find / create a connection to
1027 * @param[in] bind_dn to make the connection as
1028 * @return
1029 * - State of a trunk matching the URI and bind DN
1030 * - TRUNK_STATE_MAX if no matching trunk
1031 */
1032trunk_state_t fr_thread_ldap_trunk_state(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn)
1033{
1034 fr_ldap_thread_trunk_t *found, find = {.uri = uri, .bind_dn = bind_dn};
1035
1036 found = fr_rb_find(thread->trunks, &find);
1037
1038 return (found) ? found->trunk->state : TRUNK_STATE_MAX;
1039}
1040
1041/** Take pending LDAP bind auths from the queue and send them.
1042 *
1043 * @param[in] el Event list for timers.
1044 * @param[in] tconn Trunk handle.
1045 * @param[in] conn on which to send the queries
1046 * @param[in] uctx User context passed to trunk_alloc
1047 */
1048CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1050 connection_t *conn, void *uctx)
1051{
1052 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1053 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
1054 fr_ldap_thread_t *thread = ttrunk->t;
1055 trunk_request_t *treq;
1056
1057 fr_ldap_bind_auth_ctx_t *bind = NULL;
1058 int ret = 0;
1059 struct berval cred;
1060 request_t *request;
1061
1062 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
1063
1064 /* Pacify clang scan */
1065 if (!treq) return;
1066
1067 bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
1068 request = bind->request;
1069
1070 switch (bind->type) {
1071 case LDAP_BIND_SIMPLE:
1072 {
1073 fr_ldap_bind_ctx_t *bind_ctx = bind->bind_ctx;
1074
1075 RDEBUG2("Starting bind auth operation as %s", bind_ctx->bind_dn);
1076
1077 if (bind_ctx->password) {
1078 memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
1079 cred.bv_len = talloc_strlen(bind_ctx->password);
1080 } else {
1081 cred.bv_val = NULL;
1082 cred.bv_len = 0;
1083 }
1084
1085 ret = ldap_sasl_bind(ldap_conn->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE,
1086 &cred, NULL, NULL, &bind->msgid);
1087
1088 switch (ret) {
1089 case LDAP_SUCCESS:
1090 fr_rb_insert(thread->binds, bind);
1091 RDEBUG3("Bind auth sent as LDAP msgid %d", bind->msgid);
1092 break;
1093
1094 default:
1095 bind->ret = LDAP_PROC_ERROR;
1096 unlang_interpret_mark_runnable(treq->request);
1097 RERROR("Failed to send bind auth");
1098 break;
1099 }
1100 }
1101 break;
1102
1103#ifdef WITH_SASL
1104 case LDAP_BIND_SASL:
1105 {
1106 fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
1107
1108 RDEBUG2("%s SASL bind auth operation as %s", sasl_ctx->rmech ? "Continuing" : "Starting",
1109 sasl_ctx->identity);
1110
1111 ret = fr_ldap_sasl_bind_auth_send(sasl_ctx, &bind->msgid, ldap_conn);
1112
1113 switch (ret) {
1114 case LDAP_SASL_BIND_IN_PROGRESS:
1115 /*
1116 * Add the bind to the list of pending binds.
1117 */
1118 fr_rb_insert(thread->binds, bind);
1119 RDEBUG3("SASL bind auth sent as LDAP msgid %d", bind->msgid);
1120 break;
1121
1122 case LDAP_SUCCESS:
1123 bind->ret = LDAP_PROC_SUCCESS;
1124 unlang_interpret_mark_runnable(treq->request);
1125 break;
1126
1127 default:
1128 bind->ret = LDAP_PROC_ERROR;
1129 unlang_interpret_mark_runnable(treq->request);
1130 RERROR("Failed to send SASL bind auth");
1131 break;
1132 }
1133 }
1134#endif
1135 break;
1136 }
1137 /*
1138 * The request is marked as sent, to remove from the pending list.
1139 * This is regardless of whether the sending was successful or not as
1140 * the different states are handled by the resume function which then
1141 * marks the request as complete triggering the tidy up.
1142 */
1144}
1145
1146/** Read LDAP bind auth responses
1147 *
1148 * @param[in] el To insert timers into.
1149 * @param[in] tconn Trunk connection associated with these results.
1150 * @param[in] conn Connection handle for these results.
1151 * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
1152 */
1153CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1155 connection_t *conn, void *uctx)
1156{
1157 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1158 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
1159 fr_ldap_thread_t *thread = ttrunk->t;
1160 fr_ldap_bind_auth_ctx_t find = { .msgid = -1 }, *bind = NULL;
1161
1162 int ret = 0;
1163 LDAPMessage *result = NULL;
1164 request_t *request;
1165 bool really_no_result = false;
1166
1167 do {
1168 /*
1169 * The first time ldap_result is called when there's pending network
1170 * data, it may read the data, but actually return a timeout.
1171 *
1172 * In order to fix the spurious debugging messages and overhead,
1173 * if this is the first iteration through the loop and fr_ldap_result
1174 * returns a timeout, we call it again.
1175 */
1176 ret = fr_ldap_result(&result, NULL, ldap_conn, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(10));
1177 if (ret == LDAP_PROC_TIMEOUT) {
1178 if (really_no_result) return;
1179 really_no_result = true;
1180 continue;
1181 }
1182
1183 if (!result) return;
1184
1185 really_no_result = true;
1186 find.msgid = ldap_msgid(result);
1187 bind = fr_rb_find(thread->binds, &find);
1188
1189 if (!bind) {
1190 WARN("Ignoring bind result msgid %i - doesn't match any outstanding binds", find.msgid);
1191 ldap_msgfree(result);
1192 result = NULL;
1193 continue;
1194 }
1195 } while (!bind);
1196
1197 /*
1198 * There will only ever be one bind in flight at a time on a given
1199 * connection - so having got a result, no need to loop.
1200 */
1201
1202 fr_rb_remove(thread->binds, bind);
1203 request = bind->request;
1204 bind->ret = ret;
1205
1206 switch (ret) {
1207 /*
1208 * Accept or reject will be SUCCESS, NOT_PERMITTED or REJECT
1209 */
1211 case LDAP_PROC_REJECT:
1212 case LDAP_PROC_BAD_DN:
1214 break;
1215
1216 case LDAP_PROC_SUCCESS:
1217 if (bind->type == LDAP_BIND_SIMPLE) break;
1218
1219 /*
1220 * With SASL binds, we will be here after ldap_sasl_interactive_bind
1221 * returned LDAP_SASL_BIND_IN_PROGRESS. That always requires a further
1222 * call of ldap_sasl_interactive_bind to get the final result.
1223 */
1224 bind->ret = LDAP_PROC_CONTINUE;
1226
1227 case LDAP_PROC_CONTINUE:
1228 {
1229 fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
1230 struct berval *srv_cred;
1231
1232 /*
1233 * Free any previous result and track the new one.
1234 */
1235 if (sasl_ctx->result) ldap_msgfree(sasl_ctx->result);
1236 sasl_ctx->result = result;
1237 result = NULL;
1238
1239 ret = ldap_parse_sasl_bind_result(ldap_conn->handle, sasl_ctx->result, &srv_cred, 0);
1240 if (ret != LDAP_SUCCESS) {
1241 RERROR("SASL decode failed (bind failed): %s", ldap_err2string(ret));
1242 break;
1243 }
1244
1245 if (srv_cred) {
1246 RDEBUG3("SASL response : %pV",
1247 fr_box_strvalue_len(srv_cred->bv_val, srv_cred->bv_len));
1248 ber_bvfree(srv_cred);
1249 }
1250
1251 if (sasl_ctx->rmech) RDEBUG3("Continuing SASL mech %s...", sasl_ctx->rmech);
1252 }
1253 break;
1254
1255 default:
1256 break;
1257 }
1258
1259 ldap_msgfree(result);
1261}
1262
1263/** Callback to cancel LDAP bind auth
1264 *
1265 * Inform the remote LDAP server that we no longer want responses to specific bind.
1266 *
1267 * @param[in] el For timer management.
1268 * @param[in] tconn The trunk connection handle
1269 * @param[in] conn The specific connection binds will be cancelled on
1270 * @param[in] uctx Context provided to trunk_alloc
1271 */
1272CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1274 connection_t *conn, UNUSED void *uctx)
1275{
1276 trunk_request_t *treq;
1277 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1279
1280 while ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
1281 bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
1282#ifdef WITH_SASL
1283 if (bind->type == LDAP_BIND_SASL) {
1284 /*
1285 * With SASL binds, abandoning the bind part way through
1286 * seems to leave the connection in an unpredictable state
1287 * so safer to restart.
1288 */
1290 } else {
1291#endif
1292 ldap_abandon_ext(ldap_conn->handle, bind->msgid, NULL, NULL);
1293#ifdef WITH_SASL
1294 }
1295#endif
1297 }
1298}
1299
1300/** Callback to tidy up when a bind auth trunk request fails
1301 *
1302 */
1303static void ldap_trunk_bind_auth_fail(request_t *request, void *preq, UNUSED void *rctx,
1304 UNUSED trunk_request_state_t state, UNUSED void *uctx)
1305{
1306 fr_ldap_bind_auth_ctx_t *bind = talloc_get_type_abort(preq, fr_ldap_bind_auth_ctx_t);
1307
1308 /*
1309 * Failed trunk requests get freed - so remove association in bind structure,
1310 * and change talloc parentage so resume function still has something to work with.
1311 */
1312 bind->treq = NULL;
1313 bind->ret = LDAP_PROC_ERROR;
1314 talloc_steal(NULL, bind);
1315
1316 /*
1317 * Ensure request is runnable.
1318 */
1319 if (request) unlang_interpret_mark_runnable(request);
1320}
1321
1322/** Find the thread specific trunk to use for LDAP bind auths
1323 *
1324 * If there is no current trunk then a new one is created.
1325 *
1326 * @param[in] thread to which the connection belongs
1327 * @return
1328 * - an existing or new trunk.
1329 * - NULL on failure
1330 */
1332{
1333 fr_ldap_thread_trunk_t *ttrunk;
1334
1335 if (thread->bind_trunk) return (thread->bind_trunk);
1336
1337 MEM(ttrunk = talloc_zero(thread, fr_ldap_thread_trunk_t));
1338 memcpy(&ttrunk->config, thread->config, sizeof(fr_ldap_config_t));
1339
1340 ttrunk->uri = ttrunk->config.server;
1341 ttrunk->bind_dn = ttrunk->config.admin_identity;
1342
1343 ttrunk->trunk = trunk_alloc(ttrunk, thread->el,
1345 .connection_alloc = ldap_trunk_connection_alloc,
1346 .connection_notify = ldap_trunk_connection_notify,
1347 .request_mux = ldap_trunk_bind_auth_mux,
1348 .request_demux = ldap_trunk_bind_auth_demux,
1349 .request_cancel_mux = ldap_bind_auth_cancel_mux,
1350 .request_fail = ldap_trunk_bind_auth_fail,
1351 },
1352 thread->bind_trunk_conf,
1353 "rlm_ldap bind auth", ttrunk, false, thread->bind_trigger_args);
1354
1355 if (!ttrunk->trunk) {
1356 ERROR("Unable to create LDAP connection");
1357 talloc_free(ttrunk);
1358 return NULL;
1359 }
1360
1361 ttrunk->t = thread;
1362 thread->bind_trunk = ttrunk;
1363
1364 return ttrunk;
1365}
#define USES_APPLE_DEPRECATED_API
Definition build.h:499
#define RCSID(id)
Definition build.h:512
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:343
#define DIAG_ON(_x)
Definition build.h:487
#define CC_NO_UBSAN(_sanitize)
Definition build.h:455
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define DIAG_OFF(_x)
Definition build.h:486
connection_state_t
Definition connection.h:47
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:56
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:57
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:52
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:89
Holds a complete set of functions for a connection.
Definition connection.h:199
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:148
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static void * fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition dlist.h:860
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:2002
fr_ldap_control_t serverctrls[LDAP_MAX_CONTROLS]
Server controls specific to this query.
Definition base.h:453
LDAPURLDesc * referral_url
URL for the referral.
Definition base.h:485
fr_ldap_thread_t * t
Thread this connection is associated with.
Definition base.h:409
fr_ldap_rcode_t ret
Return code of bind operation.
Definition base.h:627
fr_ldap_config_t * config
Module instance config.
Definition base.h:384
void fr_ldap_control_clear(fr_ldap_connection_t *conn)
Clear and free any controls associated with a connection.
Definition control.c:134
bool chase_referrals
If the LDAP server returns a referral to another server or point in the tree, follow it,...
Definition base.h:250
int msgid
libldap msgid for this bind.
Definition base.h:620
char * server
Initial server to bind to.
Definition base.h:234
LDAP * handle
libldap handle.
Definition base.h:343
int msgid
The unique identifier for this query.
Definition base.h:456
char const * dn
Base DN for searches, DN for modifications.
Definition base.h:432
char const * bind_dn
DN connection is bound as.
Definition base.h:405
void fr_ldap_control_merge(LDAPControl *serverctrls_out[], LDAPControl *clientctrls_out[], size_t serverctrls_len, size_t clientctrls_len, fr_ldap_connection_t *conn, LDAPControl *serverctrls_in[], LDAPControl *clientctrls_in[])
Merge connection and call specific client and server controls.
Definition control.c:48
fr_rb_node_t node
Entry in the tree of connections.
Definition base.h:403
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition base.h:239
fr_ldap_result_code_t ret
Result code.
Definition base.h:473
fr_ldap_directory_t * fr_ldap_directory_alloc(TALLOC_CTX *ctx)
Allocate a directory structure with defaults.
Definition directory.c:272
fr_rb_tree_t * trunks
Tree of LDAP trunks used by this thread.
Definition base.h:383
int fr_ldap_referral_follow(fr_ldap_thread_t *thread, request_t *request, fr_ldap_query_t *query)
Follow an LDAP referral.
Definition referral.c:113
trunk_conf_t * trunk_conf
Module trunk config.
Definition base.h:385
fr_rb_tree_t * queries
Outstanding queries on this connection.
Definition base.h:361
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:352
fr_ldap_state_t
LDAP connection handle states.
Definition base.h:167
@ FR_LDAP_STATE_ERROR
Connection is in an error state.
Definition base.h:173
char const * identity
of the user.
Definition base.h:512
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:459
fr_dlist_head_t refs
Replied to queries still referencing this connection.
Definition base.h:362
int fd
File descriptor for this connection.
Definition base.h:359
fr_timer_t * ev
Event to close the thread when it has been idle.
Definition base.h:410
char const * uri
Server URI for this connection.
Definition base.h:404
LDAPMessage * result
Previous result.
Definition base.h:520
fr_time_delta_t net_timeout
How long we wait in blocking network calls.
Definition base.h:313
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:354
request_t * request
this bind relates to.
Definition base.h:621
fr_ldap_control_t clientctrls[LDAP_MAX_CONTROLS]
Client controls specific to this query.
Definition base.h:454
@ LDAP_BIND_SIMPLE
Definition base.h:525
trunk_request_t * treq
Trunk request this bind is associated with.
Definition base.h:619
static int8_t fr_ldap_query_cmp(void const *one, void const *two)
Compare two ldap query structures on msgid.
Definition base.h:714
fr_ldap_config_t config
Config used for this connection.
Definition base.h:406
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition state.c:50
@ LDAP_REQUEST_MODIFY
A modification to an LDAP entity.
Definition base.h:181
@ LDAP_REQUEST_SEARCH
A lookup in an LDAP directory.
Definition base.h:180
@ LDAP_REQUEST_DELETE
A deletion of an LDAP entity.
Definition base.h:182
@ LDAP_REQUEST_EXTENDED
An extended LDAP operation.
Definition base.h:183
fr_pair_list_t * bind_trigger_args
Passed to trigger request for bind trunks.
Definition base.h:391
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:460
@ LDAP_RESULT_EXCESS_REFERRALS
The referral chain took too many hops.
Definition base.h:198
@ LDAP_RESULT_REFERRAL_FAIL
Initial results indicated a referral was needed but the referral could not be followed.
Definition base.h:196
@ LDAP_RESULT_ERROR
A general error occurred.
Definition base.h:192
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:191
@ LDAP_RESULT_PENDING
Result not yet returned.
Definition base.h:190
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:195
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:194
@ LDAP_RESULT_MISSING_REFERRAL
A referral was indicated but no URL was provided.
Definition base.h:199
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition base.h:94
trunk_conf_t * bind_trunk_conf
Trunk config for bind auth trunk.
Definition base.h:386
fr_time_delta_t reconnection_delay
How long to wait before attempting to reconnect.
Definition base.h:321
uint16_t referral_depth
How many referrals to chase.
Definition base.h:257
fr_event_list_t * el
Thread event list for callbacks / timeouts.
Definition base.h:387
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:407
char const * rmech
Mech we're continuing with.
Definition base.h:521
fr_ldap_thread_trunk_t * bind_trunk
LDAP trunk used for bind auths.
Definition base.h:388
char const * bind_dn
of the user, may be NULL to bind anonymously.
Definition base.h:498
trunk_t * trunk
Connection trunk.
Definition base.h:408
fr_pair_list_t * trigger_args
Passed to trigger request for normal trunks.
Definition base.h:390
connection_t * conn
Connection state handle.
Definition base.h:355
fr_ldap_referral_t * referral
Referral actually being followed.
Definition base.h:467
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition base.h:389
fr_ldap_bind_type_t type
type of bind.
Definition base.h:622
int fr_ldap_referral_next(fr_ldap_thread_t *thread, request_t *request, fr_ldap_query_t *query)
Follow an alternative LDAP referral.
Definition referral.c:310
char const * password
of the user, may be NULL if no password is specified.
Definition base.h:499
fr_time_delta_t idle_timeout
How long to wait before closing unused connections.
Definition base.h:323
fr_ldap_request_type_t type
What type of query this is.
Definition base.h:451
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:585
@ LDAP_PROC_CONTINUE
Operation is in progress.
Definition base.h:587
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:588
@ LDAP_PROC_REFERRAL
LDAP server returned referral URLs.
Definition base.h:586
@ LDAP_PROC_TIMEOUT
Operation timed out.
Definition base.h:605
@ LDAP_PROC_ERROR
Unrecoverable library/server error.
Definition base.h:590
@ LDAP_PROC_BAD_CONN
Transitory error, caller should retry the operation with a new connection.
Definition base.h:592
@ LDAP_PROC_NOT_PERMITTED
Operation was not permitted, either current user was locked out in the case of binds,...
Definition base.h:595
@ LDAP_PROC_REJECT
Bind failed, user was rejected.
Definition base.h:599
@ LDAP_PROC_BAD_DN
Specified an invalid object in a bind or search DN.
Definition base.h:601
@ LDAP_PROC_NO_RESULT
Got no results.
Definition base.h:603
Holds arguments for async bind auth requests.
Definition base.h:616
Holds arguments for the async bind operation.
Definition base.h:496
Connection configuration.
Definition base.h:231
Tracks the state of a libldap connection handle.
Definition base.h:342
LDAP query structure.
Definition base.h:425
Holds arguments for the async SASL bind operation.
Definition base.h:509
Thread specific structure to manage LDAP trunk connections.
Definition base.h:382
Thread LDAP trunk structure.
Definition base.h:402
static void ldap_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Callback to cancel LDAP queries.
Definition connection.c:503
static void ldap_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Take LDAP pending queries from the queue and send them.
Definition connection.c:590
#define do_ldap_option(_option, _name, _value)
static int _ldap_connection_free(fr_ldap_connection_t *c)
Close and delete a connection.
Definition connection.c:221
fr_ldap_thread_trunk_t * fr_thread_ldap_trunk_get(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn, char const *bind_password, request_t *request, fr_ldap_config_t const *config)
Find a thread specific LDAP connection for a specific URI / bind DN.
Definition connection.c:940
static void ldap_trunk_bind_auth_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, void *uctx)
Take pending LDAP bind auths from the queue and send them.
static void ldap_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Callback to tidy up when a trunk request fails.
Definition connection.c:521
static USES_APPLE_DEPRECATED_API char const * ldap_msg_types[UINT8_MAX+1]
Definition connection.c:34
static void ldap_trunk_bind_auth_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Callback to tidy up when a bind auth trunk request fails.
static connection_t * ldap_trunk_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, UNUSED connection_conf_t const *conn_conf, char const *log_prefix, void *uctx)
Allocate an LDAP trunk connection.
Definition connection.c:549
fr_ldap_thread_trunk_t * fr_thread_ldap_bind_trunk_get(fr_ldap_thread_t *thread)
Find the thread specific trunk to use for LDAP bind auths.
trunk_state_t fr_thread_ldap_trunk_state(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn)
Lookup the state of a thread specific LDAP connection trunk for a specific URI / bind DN.
static void _ldap_connection_close_watch(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Watcher for LDAP connections being closed.
Definition connection.c:286
#define maybe_ldap_option(_option, _name, _value)
static int _thread_ldap_trunk_free(fr_ldap_thread_trunk_t *ttrunk)
Definition connection.c:919
connection_state_t fr_ldap_connection_init(void **h, connection_t *conn, void *uctx)
Initialise a standalone LDAP connection.
Definition connection.c:391
static void _ldap_trunk_idle_timeout(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Callback for closing idle LDAP trunk.
Definition connection.c:462
fr_ldap_connection_t * fr_ldap_connection_alloc(TALLOC_CTX *ctx)
Allocate our ldap connection handle layer.
Definition connection.c:258
static void ldap_trunk_bind_auth_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn, connection_t *conn, void *uctx)
Read LDAP bind auth responses.
static void ldap_trunk_request_demux(fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, void *uctx)
Read LDAP responses.
Definition connection.c:711
static connection_state_t ldap_connection_init(void **h, connection_t *conn, fr_ldap_config_t const *config, fr_ldap_directory_t *directory)
(Re-)Initialises the libldap side of the connection handle
Definition connection.c:343
#define POPULATE_LDAP_CONTROLS(_dest, _src)
Definition connection.c:574
int fr_ldap_connection_timeout_set(fr_ldap_connection_t const *c, fr_time_delta_t timeout)
Definition connection.c:415
int fr_ldap_connection_configure(fr_ldap_connection_t *c, fr_ldap_config_t const *config)
Allocate and configure a new connection.
Definition connection.c:67
int fr_ldap_connection_timeout_reset(fr_ldap_connection_t const *c)
Definition connection.c:437
connection_state_t fr_ldap_trunk_connection_init(void **h, connection_t *conn, void *uctx)
Initialise an LDAP trunk connection.
Definition connection.c:408
void fr_ldap_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
Free the handle, closing the connection to ldap.
Definition connection.c:195
static void ldap_bind_auth_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Callback to cancel LDAP bind auth.
static void ldap_request_cancel(UNUSED connection_t *conn, void *preq, UNUSED trunk_cancel_reason_t reason, UNUSED void *uctx)
Callback when an LDAP trunk request is cancelled.
Definition connection.c:483
fr_ldap_rcode_t fr_ldap_error_check(LDAPControl ***ctrls, fr_ldap_connection_t const *conn, LDAPMessage *msg, char const *dn)
Perform basic parsing of multiple types of messages, checking for error conditions.
Definition base.c:232
fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Search for something in the LDAP directory.
Definition base.c:529
fr_ldap_rcode_t fr_ldap_result(LDAPMessage **result, LDAPControl ***ctrls, fr_ldap_connection_t const *conn, int msgid, int all, char const *dn, fr_time_delta_t timeout)
Parse response from LDAP server dealing with any errors.
Definition base.c:450
fr_ldap_rcode_t fr_ldap_modify_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, LDAPMod *mods[], LDAPControl **serverctrls, LDAPControl **clientctrls)
Modify something in the LDAP directory.
Definition base.c:820
fr_ldap_rcode_t fr_ldap_extended_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *reqoid, struct berval *reqdata)
Initiate an LDAP extended operation.
Definition base.c:949
fr_ldap_rcode_t fr_ldap_delete_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, LDAPControl **serverctrls, LDAPControl **clientctrls)
Modify something in the LDAP directory.
Definition base.c:860
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG3(_fmt,...)
Definition log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:540
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
Stores all information relating to an event list.
Definition event.c:377
#define UINT8_MAX
static const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:781
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:695
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:244
static bool fr_rb_node_inline_in_tree(fr_rb_node_t const *node)
Check to see if an item is in a tree by examining its inline fr_rb_node_t.
Definition rb.h:312
int fr_ldap_sasl_bind_auth_send(fr_ldap_sasl_ctx_t *sasl_ctx, int *msgid, fr_ldap_connection_t *ldap_conn)
Send a SASL LDAP auth bind.
Definition sasl.c:367
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
connection_watch_entry_t * connection_add_watch_pre(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed before a state function has been called.
Definition connection.c:520
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
Definition time.h:656
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#define FR_TIMER_DELETE(_ev_p)
Definition timer.h:103
#define fr_timer_in(...)
Definition timer.h:87
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition trunk.c:3898
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2176
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2328
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition trunk.c:3946
trunk_t * trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_io_funcs_t const *funcs, trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start, fr_pair_list_t *trigger_args)
Allocate a new collection of connections.
Definition trunk.c:5077
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2094
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2138
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition trunk.c:4062
Associates request queues with a connection.
Definition trunk.c:133
Wraps a normal request.
Definition trunk.c:99
#define TRUNK_NOTIFY_FUNC(_name, _type)
Helper macro for building generic trunk notify callback.
Definition trunk.h:968
uint16_t start
How many connections to start.
Definition trunk.h:237
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition trunk.h:55
trunk_state_t
Definition trunk.h:62
@ TRUNK_STATE_MAX
Definition trunk.h:75
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition trunk.h:171
Common configuration parameters for a trunk.
Definition trunk.h:234
I/O functions to pass to trunk_alloc.
Definition trunk.h:746
static fr_event_list_t * el
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309