The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
connection.c
Go to the documentation of this file.
1/*
2 * This program is 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: 0e481afde1a5c9d991a0adc44bf02d5e9139e083 $
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: 0e481afde1a5c9d991a0adc44bf02d5e9139e083 $")
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] = {
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 */
195static void _ldap_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
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->handle, c);
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] uctx Our LDAP connection handle (a #fr_ldap_connection_t).
337 * @return
338 * - CONNECTION_STATE_CONNECTING on success.
339 * - CONNECTION_STATE_FAILED on failure.
340 */
341CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
342static connection_state_t _ldap_connection_init(void **h, connection_t *conn, void *uctx)
343{
344 fr_ldap_config_t const *config = uctx;
346 fr_ldap_state_t state;
347
348 c = fr_ldap_connection_alloc(conn);
349 c->conn = conn;
350 /*
351 * Initialise tree for outstanding queries handled by this connection
352 */
355
356 /*
357 * Configure/allocate the libldap handle
358 */
360 error:
361 talloc_free(c);
363 }
364
365 /* Don't block */
366 if (ldap_set_option(c->handle, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON) != LDAP_OPT_SUCCESS) goto error;
367 fr_ldap_connection_timeout_set(c, fr_time_delta_wrap(0)); /* Forces LDAP_X_CONNECTING */
368
369 state = fr_ldap_state_next(c);
370 if (state == FR_LDAP_STATE_ERROR) goto error;
371
373
374 *h = c; /* Set the handle */
375
377}
378
379/** Alloc a self re-establishing connection to an LDAP server
380 *
381 * @param[in] ctx to allocate any memory in, and to bind the lifetime of the connection to.
382 * @param[in] el to insert I/O and timer callbacks into.
383 * @param[in] config to use to bind the connection to an LDAP server.
384 * @param[in] log_prefix to prepend to connection state messages.
385 */
387 fr_ldap_config_t const *config, char const *log_prefix)
388{
389 connection_t *conn;
390
391 conn = connection_alloc(ctx, el,
393 .init = _ldap_connection_init,
395 },
397 .connection_timeout = config->net_timeout,
398 .reconnection_delay = config->reconnection_delay
399 },
400 log_prefix, config);
401 if (!conn) {
402 PERROR("Failed allocating state handler for new LDAP connection");
403 return NULL;
404 }
405
406 return conn;
407}
408
410{
411 int ldap_errno;
412
413 /*
414 * A value of zero results in an handle configuration failure.
415 *
416 * When most people specify zero they mean infinite.
417 *
418 * libldap requires tv_sec to be -1 to mean that.
419 */
420 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
421 (fr_time_delta_ispos(timeout) ?
422 &fr_time_delta_to_timeval(timeout) :
423 &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
424
425 return 0;
426
427error:
428 return -1;
429}
430
432{
433 int ldap_errno;
434
435 /*
436 * A value of zero results in an handle configuration failure.
437 *
438 * When most people specify zero they mean infinite.
439 *
440 * libldap requires tv_sec to be -1 to mean that.
441 */
442 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout",
445 &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
446
447 return 0;
448
449error:
450 return -1;
451}
452
453/** Callback for closing idle LDAP trunk
454 *
455 */
457{
458 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
459
460 if (ttrunk->trunk->req_alloc == 0) {
461 DEBUG2("Removing idle LDAP trunk to \"%s\"", ttrunk->uri);
462 talloc_free(ttrunk->trunk);
463 talloc_free(ttrunk);
464 } else {
465 /*
466 * There are still pending queries - insert a new event
467 */
468 (void) fr_event_timer_in(ttrunk, el, &ttrunk->ev, ttrunk->t->config->idle_timeout,
470 }
471}
472
473/** Callback when an LDAP trunk request is cancelled
474 *
475 * Ensure the request is removed from the list of outstanding requests
476 */
478 UNUSED void *uctx) {
479 fr_ldap_query_t *query = talloc_get_type_abort(preq, fr_ldap_query_t);
480
481 if (query->ldap_conn) {
482 fr_rb_remove(query->ldap_conn->queries, query);
483 query->ldap_conn = NULL;
484 }
485}
486
487/** Callback to cancel LDAP queries
488 *
489 * Inform the remote LDAP server that we no longer want responses to specific queries.
490 *
491 * @param[in] el For timer management.
492 * @param[in] tconn The trunk connection handle
493 * @param[in] conn The specific connection queries will be cancelled on
494 * @param[in] uctx Context provided to trunk_alloc
495 */
496CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
498 connection_t *conn, UNUSED void *uctx)
499{
500 trunk_request_t *treq;
501 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
502 fr_ldap_query_t *query;
503
504 while ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
505 query = talloc_get_type_abort(treq->preq, fr_ldap_query_t);
506 ldap_abandon_ext(ldap_conn->handle, query->msgid, NULL, NULL);
507
509 }
510}
511
512/** Callback to tidy up when a trunk request fails
513 *
514 */
515static void ldap_request_fail(request_t *request, void *preq, UNUSED void *rctx,
516 UNUSED trunk_request_state_t state, UNUSED void *uctx)
517{
518 fr_ldap_query_t *query = talloc_get_type_abort(preq, fr_ldap_query_t);
519
520 /*
521 * Failed trunk requests get freed - so remove association in query.
522 */
523 query->treq = NULL;
524 query->ret = LDAP_RESULT_ERROR;
525
526 /*
527 * Ensure request is runnable.
528 */
529 if (request) unlang_interpret_mark_runnable(request);
530}
531
532TRUNK_NOTIFY_FUNC(ldap_trunk_connection_notify, fr_ldap_connection_t)
533
534/** Allocate an LDAP trunk connection
535 *
536 * @param[in] tconn Trunk handle.
537 * @param[in] el Event list which will be used for I/O and timer events.
538 * @param[in] conn_conf Configuration of the connection.
539 * @param[in] log_prefix What to prefix log messages with.
540 * @param[in] uctx User context passed to trunk_alloc.
541 */
542CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
544 UNUSED connection_conf_t const *conn_conf,
545 char const *log_prefix, void *uctx)
546{
547 fr_ldap_thread_trunk_t *thread_trunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
548
549 return fr_ldap_connection_state_alloc(tconn, el, &thread_trunk->config, log_prefix);
550}
551
552#define POPULATE_LDAP_CONTROLS(_dest, _src) do { \
553 int i; \
554 for (i = 0; (i < LDAP_MAX_CONTROLS) && (_src[i].control); i++) { \
555 _dest[i] = _src[i].control; \
556 } \
557 _dest[i] = NULL; \
558} while (0)
559
560/** Take LDAP pending queries from the queue and send them.
561 *
562 * @param[in] el Event list for timers.
563 * @param[in] tconn Trunk handle.
564 * @param[in] conn on which to send the queries
565 * @param[in] uctx User context passed to trunk_alloc
566 */
567CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
569 connection_t *conn, UNUSED void *uctx)
570{
571 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
572 trunk_request_t *treq;
573
574 LDAPURLDesc *referral_url = NULL;
575
576 fr_ldap_query_t *query = NULL;
577 fr_ldap_rcode_t status;
578
579 while (trunk_connection_pop_request(&treq, tconn) == 0) {
580 LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS + 1];
581 LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS + 1];
582
583 if (!treq) break;
584
585 query = talloc_get_type_abort(treq->preq, fr_ldap_query_t);
586
587 switch (query->type) {
589 /*
590 * This query is a LDAP search
591 */
592 if (query->referral) referral_url = query->referral->referral_url;
593
594 POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
595 POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
596
597 /*
598 * If we are chasing a referral, referral_url will be populated and may
599 * have a base dn or scope to override the original query
600 */
601 status = fr_ldap_search_async(&query->msgid, query->treq->request, ldap_conn,
602 (referral_url && referral_url->lud_dn) ?
603 referral_url->lud_dn : query->dn,
604 (referral_url && referral_url->lud_scope) ?
605 referral_url->lud_scope : query->search.scope,
606 query->search.filter, query->search.attrs,
607 our_serverctrls, our_clientctrls);
608 break;
609
611 /*
612 * Send a request to modify an object
613 */
614 POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
615 POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
616
617 status = fr_ldap_modify_async(&query->msgid, query->treq->request,
618 ldap_conn, query->dn, query->mods,
619 our_serverctrls, our_clientctrls);
620 break;
621
623 /*
624 * Send a request to delete an object
625 */
626 POPULATE_LDAP_CONTROLS(our_serverctrls, query->serverctrls);
627 POPULATE_LDAP_CONTROLS(our_clientctrls, query->clientctrls);
628
629 status = fr_ldap_delete_async(&query->msgid, query->treq->request,
630 ldap_conn, query->dn,
631 our_serverctrls, our_clientctrls);
632 break;
633
635 /*
636 * This query is an LDAP extended operation.
637 */
638 status = fr_ldap_extended_async(&query->msgid, query->treq->request, ldap_conn,
639 query->extended.reqoid, query->extended.reqdata);
640 break;
641
642 default:
643 status = LDAP_PROC_ERROR;
644 ERROR("Invalid LDAP query for trunk connection");
645 error:
648 continue;
649
650 }
651
652 if (status != LDAP_PROC_SUCCESS) goto error;
653
654 /*
655 * If the query has previously been associated with a different
656 * connection, remove that reference. Typically when following references.
657 */
658 if (query->ldap_conn) fr_dlist_remove(&query->ldap_conn->refs, query);
659
660 /*
661 * Record which connection was used for this query
662 * - results processing often needs access to an LDAP handle
663 */
664 query->ldap_conn = ldap_conn;
665
666 /*
667 * Add the query to the tree of pending queries for this trunk
668 */
669 fr_rb_insert(query->ldap_conn->queries, query);
670
672 }
673
674}
675
676/** Read LDAP responses
677 *
678 * Responses from the LDAP server will cause the fd to become readable and trigger this
679 * callback. Most LDAP search responses have multiple messages in their response - we
680 * only gather those which are complete before either following a referral or passing
681 * the head of the resulting chain of messages back.
682 *
683 * @param[in] el To insert timers into.
684 * @param[in] tconn Trunk connection associated with these results.
685 * @param[in] conn Connection handle for these results.
686 * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
687 */
688CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
690{
691 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
692 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
693
694 int ret = 0, msgtype;
695 struct timeval poll = { 0, 10 };
696 LDAPMessage *result = NULL;
697 fr_ldap_rcode_t rcode;
698 fr_ldap_query_t find = { .msgid = -1 }, *query = NULL;
699 request_t *request;
700 bool really_no_result = false;
701 trunk_request_t *treq;
702
703 /*
704 * Reset the idle timeout event
705 */
706 (void) fr_event_timer_in(ttrunk, el, &ttrunk->ev,
707 ttrunk->t->config->idle_timeout, _ldap_trunk_idle_timeout, ttrunk);
708
709 do {
710 /*
711 * Look for any results for which we have the complete result message
712 * ldap_result will return a pointer to a chain of messages.
713 *
714 * The first time ldap_result is called when there's pending network
715 * data, it may read the data, but not return any results.
716 *
717 * In order to fix the spurious debugging messages and overhead,
718 * if this is the first iteration through the loop and ldap_result
719 * returns no result (0), we call it again.
720 */
721 ret = ldap_result(ldap_conn->handle, LDAP_RES_ANY, LDAP_MSG_ALL, &poll, &result);
722 switch (ret) {
723 case 0:
724 if (really_no_result) return;
725 really_no_result = true;
726 continue;
727
728 case -1:
729 rcode = fr_ldap_error_check(NULL, ldap_conn, NULL, NULL);
730 if (rcode == LDAP_PROC_BAD_CONN) {
731 ERROR("Bad LDAP connection");
733 }
734 return;
735
736 default:
737 /*
738 * We only retry ldap_result the first time through the loop.
739 */
740 really_no_result = true;
741 break;
742 }
743
744 find.msgid = ldap_msgid(result);
745 query = fr_rb_find(ldap_conn->queries, &find);
746
747 if (!query) {
748 WARN("Ignoring msgid %i - doesn't match any outstanding queries (it may have been cancelled)",
749 find.msgid);
750 ldap_msgfree(result);
751 continue;
752 }
753
754 /*
755 * Remove the query from the tree of outstanding queries
756 */
757 fr_rb_remove(ldap_conn->queries, query);
758
759 /*
760 * Add the query to the list of queries referencing this connection.
761 * Prevents the connection from being freed until the query has finished using it.
762 */
763 fr_dlist_insert_tail(&ldap_conn->refs, query);
764
765 /*
766 * This really shouldn't happen - as we only retrieve complete sets of results -
767 * but as the query data structure will last until its results are fully handled
768 * better to have this safety check here.
769 */
770 if (query->ret != LDAP_RESULT_PENDING) {
771 WARN("Received results for msgid %i which has already been handled - ignoring", find.msgid);
772 ldap_msgfree(result);
773 continue;
774 }
775
776 msgtype = ldap_msgtype(result);
777
778 /*
779 * Request to reference in debug output
780 */
781 request = query->treq->request;
782
783 ROPTIONAL(RDEBUG2, DEBUG2, "Got %s response for message %d",
784 ldap_msg_types[msgtype], query->msgid);
785 rcode = fr_ldap_error_check(NULL, ldap_conn, result, query->dn);
786
787 switch (rcode) {
789 switch (query->type) {
791 query->ret = (ldap_count_entries(ldap_conn->handle, result) == 0) ?
793 break;
794
795 default:
796 query->ret = LDAP_RESULT_SUCCESS;
797 break;
798 }
799 break;
800
802 if (!ttrunk->t->config->chase_referrals) {
804 "LDAP referral received but 'chase_referrals' is set to 'no'");
805 query->ret = LDAP_RESULT_EXCESS_REFERRALS;
806 break;
807 }
808
809 if (query->referral_depth >= ttrunk->t->config->referral_depth) {
810 ROPTIONAL(REDEBUG, ERROR, "Maximum LDAP referral depth (%d) exceeded",
811 ttrunk->t->config->referral_depth);
812 query->ret = LDAP_RESULT_EXCESS_REFERRALS;
813 break;
814 }
815
816 /*
817 * If we've come here as the result of an existing referral
818 * clear the previous list of URLs before getting the next list.
819 */
820 if (query->referral_urls) ldap_memvfree((void **)query->referral_urls);
821
822 ldap_get_option(ldap_conn->handle, LDAP_OPT_REFERRAL_URLS, &query->referral_urls);
823 if (!(query->referral_urls) || (!(query->referral_urls[0]))) {
824 ROPTIONAL(REDEBUG, ERROR, "LDAP referral missing referral URL");
825 query->ret = LDAP_RESULT_MISSING_REFERRAL;
826 break;
827 }
828
829 query->referral_depth ++;
830
831 if (fr_ldap_referral_follow(ttrunk->t, request, query) == 0) {
832 next_follow:
833 ldap_msgfree(result);
834 continue;
835 }
836
837 ROPTIONAL(REDEBUG, ERROR, "Unable to follow any LDAP referral URLs");
838 query->ret = LDAP_RESULT_REFERRAL_FAIL;
839 break;
840
841 case LDAP_PROC_BAD_DN:
842 ROPTIONAL(RDEBUG2, DEBUG2, "DN %s does not exist", query->dn);
843 query->ret = LDAP_RESULT_BAD_DN;
844 break;
845
846 default:
847 ROPTIONAL(RPERROR, PERROR, "LDAP server returned an error");
848
849 if (query->referral_depth > 0) {
850 /*
851 * We're processing a referral - see if there are any more to try
852 */
853 fr_dlist_talloc_free_item(&query->referrals, query->referral);
854 query->referral = NULL;
855
856 if ((fr_dlist_num_elements(&query->referrals) > 0) &&
857 (fr_ldap_referral_next(ttrunk->t, request, query) == 0)) goto next_follow;
858 }
859
860 query->ret = LDAP_RESULT_REFERRAL_FAIL;
861 break;
862 }
863
864 /*
865 * Remove the timeout event
866 */
867 if (query->ev) fr_event_timer_delete(&query->ev);
868
869 query->result = result;
870
871 /*
872 * If we have a specific parser to handle the result, call it
873 */
874 if (query->parser && (rcode == LDAP_PROC_SUCCESS)) query->parser(ldap_conn->handle, query,
875 result, query->treq->rctx);
876
877 /*
878 * Set the request as runnable
879 */
880 if (request) unlang_interpret_mark_runnable(request);
881
882 /*
883 * If referral following failed, there is no active trunk request.
884 */
885 if (!query->treq) continue;
886
887 /*
888 * If the query is parented off the treq then it will be freed when
889 * the request is completed. If it is parented by something else then it will not.
890 */
891 treq = query->treq;
892 query->treq = NULL;
894 } while (1);
895}
896
898{
899 if (ttrunk->t && fr_rb_node_inline_in_tree(&ttrunk->node)) fr_rb_remove(ttrunk->t->trunks, ttrunk);
900
901 return 0;
902}
903
904/** Find a thread specific LDAP connection for a specific URI / bind DN
905 *
906 * If no existing connection exists for that combination then create a new one
907 *
908 * @param[in] thread to which the connection belongs
909 * @param[in] uri of the host to find / create a connection to
910 * @param[in] bind_dn to make the connection as
911 * @param[in] bind_password for making connection
912 * @param[in] request currently being processed (only for debug messages)
913 * @param[in] config LDAP config of the module requesting the connection.
914 * @return
915 * - an existing or new connection matching the URI and bind DN
916 * - NULL on failure
917 */
919 char const *bind_dn, char const *bind_password,
920 request_t *request, fr_ldap_config_t const *config)
921{
922 fr_ldap_thread_trunk_t *found, find = {.uri = uri, .bind_dn = bind_dn};
923
924 ROPTIONAL(RDEBUG2, DEBUG2, "Looking for LDAP connection to \"%s\" bound as \"%s\"", uri,
925 bind_dn ? bind_dn : "(anonymous)");
926 found = fr_rb_find(thread->trunks, &find);
927
928 if (found) return found;
929
930 /*
931 * No existing connection matching the requirement - create a new one
932 */
933 ROPTIONAL(RDEBUG2, DEBUG2, "No existing connection found - creating new one");
934 found = talloc_zero(thread, fr_ldap_thread_trunk_t);
935 talloc_set_destructor(found, _thread_ldap_trunk_free);
936
937 /*
938 * Build config for this connection - start with module settings and
939 * override server and bind details
940 */
941 memcpy(&found->config, config, sizeof(fr_ldap_config_t));
942 found->config.server = talloc_strdup(found, uri);
943 found->config.admin_identity = talloc_strdup(found, bind_dn);
944 found->config.admin_password = talloc_strdup(found, bind_password);
945
946 found->uri = found->config.server;
947 found->bind_dn = found->config.admin_identity;
948
949 found->trunk = trunk_alloc(found, thread->el,
951 .connection_alloc = ldap_trunk_connection_alloc,
952 .connection_notify = ldap_trunk_connection_notify,
953 .request_mux = ldap_trunk_request_mux,
954 .request_demux = ldap_trunk_request_demux,
955 .request_cancel = ldap_request_cancel,
956 .request_cancel_mux = ldap_request_cancel_mux,
957 .request_fail = ldap_request_fail,
958 },
959 thread->trunk_conf,
960 "rlm_ldap", found, false);
961
962 if (!found->trunk) {
963 error:
964 ROPTIONAL(REDEBUG, ERROR, "Unable to create LDAP connection");
965 talloc_free(found);
966 return NULL;
967 }
968
969 found->t = thread;
970
971 /*
972 * Insert event to close trunk if it becomes idle
973 */
974 if (!fr_cond_assert_msg(fr_event_timer_in(found, thread->el, &found->ev, thread->config->idle_timeout,
975 _ldap_trunk_idle_timeout, found) == 0, "cannot insert trunk idle event")) goto error;
976
977 /*
978 * Attempt to discover what type directory we are talking to
979 */
980 if (fr_ldap_trunk_directory_alloc_async(found, found) < 0) goto error;
981
982 fr_rb_insert(thread->trunks, found);
983
984 return found;
985}
986
987/** Lookup the state of a thread specific LDAP connection trunk for a specific URI / bind DN
988 *
989 * @param[in] thread to which the connection belongs
990 * @param[in] uri of the host to find / create a connection to
991 * @param[in] bind_dn to make the connection as
992 * @return
993 * - State of a trunk matching the URI and bind DN
994 * - TRUNK_STATE_MAX if no matching trunk
995 */
996trunk_state_t fr_thread_ldap_trunk_state(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn)
997{
998 fr_ldap_thread_trunk_t *found, find = {.uri = uri, .bind_dn = bind_dn};
999
1000 found = fr_rb_find(thread->trunks, &find);
1001
1002 return (found) ? found->trunk->state : TRUNK_STATE_MAX;
1003}
1004
1005/** Take pending LDAP bind auths from the queue and send them.
1006 *
1007 * @param[in] el Event list for timers.
1008 * @param[in] tconn Trunk handle.
1009 * @param[in] conn on which to send the queries
1010 * @param[in] uctx User context passed to trunk_alloc
1011 */
1012CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1014 connection_t *conn, void *uctx)
1015{
1016 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1017 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
1018 fr_ldap_thread_t *thread = ttrunk->t;
1019 trunk_request_t *treq;
1020
1021 fr_ldap_bind_auth_ctx_t *bind = NULL;
1022 int ret = 0;
1023 struct berval cred;
1024 request_t *request;
1025
1026 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
1027
1028 /* Pacify clang scan */
1029 if (!treq) return;
1030
1031 bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
1032 request = bind->request;
1033
1034 switch (bind->type) {
1035 case LDAP_BIND_SIMPLE:
1036 {
1037 fr_ldap_bind_ctx_t *bind_ctx = bind->bind_ctx;
1038
1039 RDEBUG2("Starting bind auth operation as %s", bind_ctx->bind_dn);
1040
1041 if (bind_ctx->password) {
1042 memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
1043 cred.bv_len = talloc_array_length(bind_ctx->password) - 1;
1044 } else {
1045 cred.bv_val = NULL;
1046 cred.bv_len = 0;
1047 }
1048
1049 ret = ldap_sasl_bind(ldap_conn->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE,
1050 &cred, NULL, NULL, &bind->msgid);
1051
1052 switch (ret) {
1053 case LDAP_SUCCESS:
1054 fr_rb_insert(thread->binds, bind);
1055 RDEBUG3("Bind auth sent as LDAP msgid %d", bind->msgid);
1056 break;
1057
1058 default:
1059 bind->ret = LDAP_PROC_ERROR;
1060 unlang_interpret_mark_runnable(treq->request);
1061 RERROR("Failed to send bind auth");
1062 break;
1063 }
1064 }
1065 break;
1066
1067#ifdef WITH_SASL
1068 case LDAP_BIND_SASL:
1069 {
1070 fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
1071
1072 RDEBUG2("%s SASL bind auth operation as %s", sasl_ctx->rmech ? "Continuing" : "Starting",
1073 sasl_ctx->identity);
1074
1075 ret = fr_ldap_sasl_bind_auth_send(sasl_ctx, &bind->msgid, ldap_conn);
1076
1077 switch (ret) {
1078 case LDAP_SASL_BIND_IN_PROGRESS:
1079 /*
1080 * Add the bind to the list of pending binds.
1081 */
1082 fr_rb_insert(thread->binds, bind);
1083 RDEBUG3("SASL bind auth sent as LDAP msgid %d", bind->msgid);
1084 break;
1085
1086 case LDAP_SUCCESS:
1087 bind->ret = LDAP_PROC_SUCCESS;
1088 unlang_interpret_mark_runnable(treq->request);
1089 break;
1090
1091 default:
1092 bind->ret = LDAP_PROC_ERROR;
1093 unlang_interpret_mark_runnable(treq->request);
1094 RERROR("Failed to send SASL bind auth");
1095 break;
1096 }
1097 }
1098#endif
1099 break;
1100 }
1101 /*
1102 * The request is marked as sent, to remove from the pending list.
1103 * This is regardless of whether the sending was successful or not as
1104 * the different states are handled by the resume function which then
1105 * marks the request as complete triggering the tidy up.
1106 */
1108}
1109
1110/** Read LDAP bind auth responses
1111 *
1112 * @param[in] el To insert timers into.
1113 * @param[in] tconn Trunk connection associated with these results.
1114 * @param[in] conn Connection handle for these results.
1115 * @param[in] uctx Thread specific trunk structure - contains tree of pending queries.
1116 */
1117CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1119 connection_t *conn, void *uctx)
1120{
1121 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1122 fr_ldap_thread_trunk_t *ttrunk = talloc_get_type_abort(uctx, fr_ldap_thread_trunk_t);
1123 fr_ldap_thread_t *thread = ttrunk->t;
1124 fr_ldap_bind_auth_ctx_t find = { .msgid = -1 }, *bind = NULL;
1125
1126 int ret = 0;
1127 LDAPMessage *result = NULL;
1128 request_t *request;
1129 bool really_no_result = false;
1130
1131 do {
1132 /*
1133 * The first time ldap_result is called when there's pending network
1134 * data, it may read the data, but actually return a timeout.
1135 *
1136 * In order to fix the spurious debugging messages and overhead,
1137 * if this is the first iteration through the loop and fr_ldap_result
1138 * returns a timeout, we call it again.
1139 */
1140 ret = fr_ldap_result(&result, NULL, ldap_conn, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(10));
1141 if (ret == LDAP_PROC_TIMEOUT) {
1142 if (really_no_result) return;
1143 really_no_result = true;
1144 continue;
1145 }
1146
1147 if (!result) return;
1148
1149 really_no_result = true;
1150 find.msgid = ldap_msgid(result);
1151 bind = fr_rb_find(thread->binds, &find);
1152
1153 if (!bind) {
1154 WARN("Ignoring bind result msgid %i - doesn't match any outstanding binds", find.msgid);
1155 ldap_msgfree(result);
1156 result = NULL;
1157 continue;
1158 }
1159 } while (!bind);
1160
1161 /*
1162 * There will only ever be one bind in flight at a time on a given
1163 * connection - so having got a result, no need to loop.
1164 */
1165
1166 fr_rb_remove(thread->binds, bind);
1167 request = bind->request;
1168 bind->ret = ret;
1169
1170 switch (ret) {
1171 /*
1172 * Accept or reject will be SUCCESS, NOT_PERMITTED or REJECT
1173 */
1175 case LDAP_PROC_REJECT:
1176 case LDAP_PROC_BAD_DN:
1178 break;
1179
1180 case LDAP_PROC_SUCCESS:
1181 if (bind->type == LDAP_BIND_SIMPLE) break;
1182
1183 /*
1184 * With SASL binds, we will be here after ldap_sasl_interactive_bind
1185 * returned LDAP_SASL_BIND_IN_PROGRESS. That always requires a further
1186 * call of ldap_sasl_interactive_bind to get the final result.
1187 */
1188 bind->ret = LDAP_PROC_CONTINUE;
1190
1191 case LDAP_PROC_CONTINUE:
1192 {
1193 fr_ldap_sasl_ctx_t *sasl_ctx = bind->sasl_ctx;
1194 struct berval *srv_cred;
1195
1196 /*
1197 * Free any previous result and track the new one.
1198 */
1199 if (sasl_ctx->result) ldap_msgfree(sasl_ctx->result);
1200 sasl_ctx->result = result;
1201 result = NULL;
1202
1203 ret = ldap_parse_sasl_bind_result(ldap_conn->handle, sasl_ctx->result, &srv_cred, 0);
1204 if (ret != LDAP_SUCCESS) {
1205 RERROR("SASL decode failed (bind failed): %s", ldap_err2string(ret));
1206 break;
1207 }
1208
1209 if (srv_cred) {
1210 RDEBUG3("SASL response : %pV",
1211 fr_box_strvalue_len(srv_cred->bv_val, srv_cred->bv_len));
1212 ber_bvfree(srv_cred);
1213 }
1214
1215 if (sasl_ctx->rmech) RDEBUG3("Continuing SASL mech %s...", sasl_ctx->rmech);
1216 }
1217 break;
1218
1219 default:
1220 break;
1221 }
1222
1223 ldap_msgfree(result);
1225}
1226
1227/** Callback to cancel LDAP bind auth
1228 *
1229 * Inform the remote LDAP server that we no longer want responses to specific bind.
1230 *
1231 * @param[in] el For timer management.
1232 * @param[in] tconn The trunk connection handle
1233 * @param[in] conn The specific connection binds will be cancelled on
1234 * @param[in] uctx Context provided to trunk_alloc
1235 */
1236CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1238 connection_t *conn, UNUSED void *uctx)
1239{
1240 trunk_request_t *treq;
1241 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1243
1244 while ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
1245 bind = talloc_get_type_abort(treq->preq, fr_ldap_bind_auth_ctx_t);
1246#ifdef WITH_SASL
1247 if (bind->type == LDAP_BIND_SASL) {
1248 /*
1249 * With SASL binds, abandoning the bind part way through
1250 * seems to leave the connection in an unpredictable state
1251 * so safer to restart.
1252 */
1254 } else {
1255#endif
1256 ldap_abandon_ext(ldap_conn->handle, bind->msgid, NULL, NULL);
1257#ifdef WITH_SASL
1258 }
1259#endif
1261 }
1262}
1263
1264/** Callback to tidy up when a bind auth trunk request fails
1265 *
1266 */
1267static void ldap_trunk_bind_auth_fail(request_t *request, void *preq, UNUSED void *rctx,
1268 UNUSED trunk_request_state_t state, UNUSED void *uctx)
1269{
1270 fr_ldap_bind_auth_ctx_t *bind = talloc_get_type_abort(preq, fr_ldap_bind_auth_ctx_t);
1271
1272 /*
1273 * Failed trunk requests get freed - so remove association in bind structure,
1274 * and change talloc parentage so resume function still has something to work with.
1275 */
1276 bind->treq = NULL;
1277 bind->ret = LDAP_PROC_ERROR;
1278 talloc_steal(NULL, bind);
1279
1280 /*
1281 * Ensure request is runnable.
1282 */
1283 if (request) unlang_interpret_mark_runnable(request);
1284}
1285
1286/** Find the thread specific trunk to use for LDAP bind auths
1287 *
1288 * If there is no current trunk then a new one is created.
1289 *
1290 * @param[in] thread to which the connection belongs
1291 * @return
1292 * - an existing or new trunk.
1293 * - NULL on failure
1294 */
1296{
1297 fr_ldap_thread_trunk_t *ttrunk;
1298
1299 if (thread->bind_trunk) return (thread->bind_trunk);
1300
1301 MEM(ttrunk = talloc_zero(thread, fr_ldap_thread_trunk_t));
1302 memcpy(&ttrunk->config, thread->config, sizeof(fr_ldap_config_t));
1303
1304 ttrunk->uri = ttrunk->config.server;
1305 ttrunk->bind_dn = ttrunk->config.admin_identity;
1306
1307 ttrunk->trunk = trunk_alloc(ttrunk, thread->el,
1309 .connection_alloc = ldap_trunk_connection_alloc,
1310 .connection_notify = ldap_trunk_connection_notify,
1311 .request_mux = ldap_trunk_bind_auth_mux,
1312 .request_demux = ldap_trunk_bind_auth_demux,
1313 .request_cancel_mux = ldap_bind_auth_cancel_mux,
1314 .request_fail = ldap_trunk_bind_auth_fail,
1315 },
1316 thread->bind_trunk_conf,
1317 "rlm_ldap bind auth", ttrunk, false);
1318
1319 if (!ttrunk->trunk) {
1320 ERROR("Unable to create LDAP connection");
1321 talloc_free(ttrunk);
1322 return NULL;
1323 }
1324
1325 ttrunk->t = thread;
1326 thread->bind_trunk = ttrunk;
1327
1328 return ttrunk;
1329}
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define DIAG_ON(_x)
Definition build.h:458
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define DIAG_OFF(_x)
Definition build.h:457
connection_state_t
Definition connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:54
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:55
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:50
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:84
Holds a complete set of functions for a connection.
Definition connection.h:186
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:156
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:939
static void * fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition dlist.h:876
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:378
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:62
#define fr_event_timer_in(...)
Definition event.h:255
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1359
fr_ldap_control_t serverctrls[LDAP_MAX_CONTROLS]
Server controls specific to this query.
Definition base.h:450
LDAPURLDesc * referral_url
URL for the referral.
Definition base.h:482
fr_ldap_thread_t * t
Thread this connection is associated with.
Definition base.h:406
fr_ldap_rcode_t ret
Return code of bind operation.
Definition base.h:624
char const * admin_password
Password used in administrative bind.
Definition base.h:231
fr_ldap_config_t * config
Module instance config.
Definition base.h:383
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:240
int msgid
libldap msgid for this bind.
Definition base.h:617
char * server
Initial server to bind to.
Definition base.h:224
LDAP * handle
libldap handle.
Definition base.h:333
int msgid
The unique identifier for this query.
Definition base.h:453
char const * dn
Base DN for searches, DN for modifications.
Definition base.h:429
char const * bind_dn
DN connection is bound as.
Definition base.h:402
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:400
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition base.h:229
fr_ldap_result_code_t ret
Result code.
Definition base.h:470
fr_rb_tree_t * trunks
Tree of LDAP trunks used by this thread.
Definition base.h:382
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:384
fr_rb_tree_t * queries
Outstanding queries on this connection.
Definition base.h:351
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:172
char const * identity
of the user.
Definition base.h:509
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:456
fr_event_timer_t const * ev
Event to close the thread when it has been idle.
Definition base.h:407
fr_dlist_head_t refs
Replied to queries still referencing this connection.
Definition base.h:352
int fd
File descriptor for this connection.
Definition base.h:349
int fr_ldap_trunk_directory_alloc_async(TALLOC_CTX *ctx, fr_ldap_thread_trunk_t *ttrunk)
Async extract useful information from the rootDSE of the LDAP server.
Definition directory.c:257
char const * uri
Server URI for this connection.
Definition base.h:401
LDAPMessage * result
Previous result.
Definition base.h:517
fr_time_delta_t net_timeout
How long we wait in blocking network calls.
Definition base.h:303
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:344
request_t * request
this bind relates to.
Definition base.h:618
fr_ldap_control_t clientctrls[LDAP_MAX_CONTROLS]
Client controls specific to this query.
Definition base.h:451
@ LDAP_BIND_SIMPLE
Definition base.h:522
trunk_request_t * treq
Trunk request this bind is associated with.
Definition base.h:616
static int8_t fr_ldap_query_cmp(void const *one, void const *two)
Compare two ldap query structures on msgid.
Definition base.h:711
fr_ldap_config_t config
Config used for this connection.
Definition base.h:403
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition state.c:49
@ LDAP_REQUEST_MODIFY
A modification to an LDAP entity.
Definition base.h:180
@ LDAP_REQUEST_SEARCH
A lookup in an LDAP directory.
Definition base.h:179
@ LDAP_REQUEST_DELETE
A deletion of an LDAP entity.
Definition base.h:181
@ LDAP_REQUEST_EXTENDED
An extended LDAP operation.
Definition base.h:182
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:457
@ LDAP_RESULT_EXCESS_REFERRALS
The referral chain took too many hops.
Definition base.h:197
@ LDAP_RESULT_REFERRAL_FAIL
Initial results indicated a referral was needed but the referral could not be followed.
Definition base.h:195
@ LDAP_RESULT_ERROR
A general error occurred.
Definition base.h:191
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:190
@ LDAP_RESULT_PENDING
Result not yet returned.
Definition base.h:189
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:194
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:193
@ LDAP_RESULT_MISSING_REFERRAL
A referral was indicated but no URL was provided.
Definition base.h:198
#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:385
uint16_t referral_depth
How many referrals to chase.
Definition base.h:247
fr_event_list_t * el
Thread event list for callbacks / timeouts.
Definition base.h:386
char const * rmech
Mech we're continuing with.
Definition base.h:518
fr_ldap_thread_trunk_t * bind_trunk
LDAP trunk used for bind auths.
Definition base.h:387
char const * bind_dn
of the user, may be NULL to bind anonymously.
Definition base.h:495
trunk_t * trunk
Connection trunk.
Definition base.h:405
connection_t * conn
Connection state handle.
Definition base.h:345
fr_ldap_referral_t * referral
Referral actually being followed.
Definition base.h:464
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition base.h:388
fr_ldap_bind_type_t type
type of bind.
Definition base.h:619
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:496
fr_time_delta_t idle_timeout
How long to wait before closing unused connections.
Definition base.h:313
fr_ldap_request_type_t type
What type of query this is.
Definition base.h:448
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:582
@ LDAP_PROC_CONTINUE
Operation is in progress.
Definition base.h:584
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:585
@ LDAP_PROC_REFERRAL
LDAP server returned referral URLs.
Definition base.h:583
@ LDAP_PROC_TIMEOUT
Operation timed out.
Definition base.h:602
@ LDAP_PROC_ERROR
Unrecoverable library/server error.
Definition base.h:587
@ LDAP_PROC_BAD_CONN
Transitory error, caller should retry the operation with a new connection.
Definition base.h:589
@ LDAP_PROC_NOT_PERMITTED
Operation was not permitted, either current user was locked out in the case of binds,...
Definition base.h:592
@ LDAP_PROC_REJECT
Bind failed, user was rejected.
Definition base.h:596
@ LDAP_PROC_BAD_DN
Specified an invalid object in a bind or search DN.
Definition base.h:598
@ LDAP_PROC_NO_RESULT
Got no results.
Definition base.h:600
Holds arguments for async bind auth requests.
Definition base.h:613
Holds arguments for the async bind operation.
Definition base.h:493
Connection configuration.
Definition base.h:221
Tracks the state of a libldap connection handle.
Definition base.h:332
LDAP query structure.
Definition base.h:422
Holds arguments for the async SASL bind operation.
Definition base.h:506
Thread specific structure to manage LDAP trunk connections.
Definition base.h:381
Thread LDAP trunk structure.
Definition base.h:399
connection_t * fr_ldap_connection_state_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_ldap_config_t const *config, char const *log_prefix)
Alloc a self re-establishing connection to an LDAP server.
Definition connection.c:386
static void _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_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:497
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:568
#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:918
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_trunk_idle_timeout(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Callback for closing idle LDAP trunk.
Definition connection.c:456
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:515
static connection_state_t _ldap_connection_init(void **h, connection_t *conn, void *uctx)
(Re-)Initialises the libldap side of the connection handle
Definition connection.c:342
static USES_APPLE_DEPRECATED_API char const * ldap_msg_types[UINT8_MAX]
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:543
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.
Definition connection.c:996
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:897
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:689
#define POPULATE_LDAP_CONTROLS(_dest, _src)
Definition connection.c:552
int fr_ldap_connection_timeout_set(fr_ldap_connection_t const *c, fr_time_delta_t timeout)
Definition connection.c:409
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:431
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:477
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:801
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:925
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:841
#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:528
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RERROR(fmt,...)
Definition log.h:298
#define RPERROR(fmt,...)
Definition log.h:302
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition event.c:1611
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:1260
Stores all information relating to an event list.
Definition event.c:411
#define UINT8_MAX
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
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:246
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:314
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:513
#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
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:3835
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2132
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)
Allocate a new collection of connections.
Definition trunk.c:4945
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2284
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:3883
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2050
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2094
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition trunk.c:3999
Associates request queues with a connection.
Definition trunk.c:134
Wraps a normal request.
Definition trunk.c:100
#define TRUNK_NOTIFY_FUNC(_name, _type)
Helper macro for building generic trunk notify callback.
Definition trunk.h:953
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:66
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition trunk.h:161
I/O functions to pass to trunk_alloc.
Definition trunk.h:732
static fr_event_list_t * el
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286