The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
24 RCSID("$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  */
34 static 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 
99 DIAG_OFF(unused-macros)
100 #define maybe_ldap_option(_option, _name, _value) \
101  if (_value) do_ldap_option(_option, _name, _value)
102 DIAG_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  */
195 static 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  */
341 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
342 static 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  */
354  fr_dlist_init(&c->refs, fr_ldap_query_t, entry);
355 
356  /*
357  * Configure/allocate the libldap handle
358  */
359  if (fr_ldap_connection_configure(c, config) < 0) {
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,
394  .close = _ldap_connection_close
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",
423  &(struct timeval) { .tv_sec = -1, .tv_usec = 0 }));
424 
425  return 0;
426 
427 error:
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 
449 error:
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,
469  _ldap_trunk_idle_timeout, ttrunk);
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  */
496 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
497 static void ldap_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
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  */
515 static void ldap_request_fail(request_t *request, void *preq, UNUSED void *rctx,
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 
532 TRUNK_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  */
542 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
543 static connection_t *ldap_trunk_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el,
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  */
567 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
568 static void ldap_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
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) {
588  case LDAP_REQUEST_SEARCH:
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 
610  case LDAP_REQUEST_MODIFY:
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 
622  case LDAP_REQUEST_DELETE:
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  */
688 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
689 static void ldap_trunk_request_demux(fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, void *uctx)
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) {
788  case LDAP_PROC_SUCCESS:
789  switch (query->type) {
790  case LDAP_REQUEST_SEARCH:
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 
801  case LDAP_PROC_REFERRAL:
802  if (!ttrunk->t->config->chase_referrals) {
804  "LDAP referral received but 'chase_referrals' is set to 'no'");
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);
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");
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");
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  */
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 
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,
950  &(trunk_io_funcs_t){
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  */
996 trunk_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  */
1012 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1013 static void ldap_trunk_bind_auth_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
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  */
1117 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1118 static void ldap_trunk_bind_auth_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn,
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:
1177  case LDAP_PROC_NO_RESULT:
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;
1189  FALL_THROUGH;
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  */
1236 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
1237 static void ldap_bind_auth_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
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  */
1267 static 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,
1308  &(trunk_io_funcs_t){
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:468
#define RCSID(id)
Definition: build.h:481
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define DIAG_ON(_x)
Definition: build.h:456
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define DIAG_OFF(_x)
Definition: build.h:455
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
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#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 ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
static void * fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition: dlist.h:876
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 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
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
@ 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
fr_event_timer_t const * ev
Event for timing out the query.
Definition: base.h:459
bool chase_referrals
If the LDAP server returns a referral to another server or point in the tree, follow it,...
Definition: base.h:240
char ** referral_urls
Referral results to follow.
Definition: base.h:461
int msgid
libldap msgid for this bind.
Definition: base.h:617
char * server
Initial server to bind to.
Definition: base.h:224
uint16_t referral_depth
How many referrals we have followed.
Definition: base.h:463
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
fr_dlist_head_t referrals
List of parsed referrals.
Definition: base.h:462
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_ldap_result_parser_t parser
Custom results parser.
Definition: base.h:466
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
LDAPMessage * result
Head of LDAP results list.
Definition: base.h:468
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
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
fr_ldap_connection_t * fr_ldap_connection_alloc(TALLOC_CTX *ctx)
Allocate our ldap connection handle layer.
Definition: connection.c:258
#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
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 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.
Definition: connection.c:1267
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.
Definition: connection.c:1295
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
CC_NO_UBSAN(function)
(Re-)Initialises the libldap side of the connection handle
Definition: connection.c:341
#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_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:528
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:449
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:800
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:924
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:840
#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
Definition: merged_model.c:32
static const conf_parser_t config[]
Definition: base.c:183
#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)
#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
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
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.
Definition: connection.c:1167
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:510
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.
Definition: connection.c:1512
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
#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:3813
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition: trunk.c:2272
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:3861
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:2043
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition: trunk.c:2087
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition: trunk.c:3977
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:4885
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
#define TRUNK_NOTIFY_FUNC(_name, _type)
Helper macro for building generic trunk notify callback.
Definition: trunk.h:945
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:724
static fr_event_list_t * el
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:286