The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_ldap.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: fc789e2174ba69fff74e64324902c39ebeeafd2e $
19  * @file rlm_ldap.c
20  * @brief LDAP authorization and authentication module.
21  *
22  * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  * @author Alan DeKok (aland@freeradius.org)
24  *
25  * @copyright 2012,2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26  * @copyright 2013,2015 Network RADIUS SAS (legal@networkradius.com)
27  * @copyright 2012 Alan DeKok (aland@freeradius.org)
28  * @copyright 1999-2013 The FreeRADIUS Server Project.
29  */
30 RCSID("$Id: fc789e2174ba69fff74e64324902c39ebeeafd2e $")
31 
33 
34 #include <freeradius-devel/util/debug.h>
35 #include <freeradius-devel/util/table.h>
36 #include <freeradius-devel/util/uri.h>
37 #include <freeradius-devel/util/value.h>
38 
39 #include <freeradius-devel/ldap/conf.h>
40 #include <freeradius-devel/ldap/base.h>
41 
42 #include <freeradius-devel/server/map_proc.h>
43 #include <freeradius-devel/server/module_rlm.h>
44 #include <freeradius-devel/server/rcode.h>
45 
46 #include <freeradius-devel/unlang/xlat_func.h>
47 #include <freeradius-devel/unlang/action.h>
48 
49 #include <ldap.h>
50 #include "rlm_ldap.h"
51 
52 typedef struct {
62 
63 typedef struct {
67 
68 /** Call environment used in the profile xlat
69  */
70 typedef struct {
71  fr_value_box_t profile_filter; //!< Filter to use when searching for users.
72  map_list_t *profile_map; //!< List of maps to apply to the profile.
74 
75 static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
76 
77 static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, void const *data, UNUSED call_env_parser_t const *rule);
78 
79 static const call_env_parser_t sasl_call_env[] = {
81  { FR_CALL_ENV_OFFSET("authname", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, ldap_auth_call_env_t, user_sasl_authname) },
85 };
86 
88  { FR_CONF_OFFSET("scope", rlm_ldap_t, profile_scope), .dflt = "base",
89  .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = fr_ldap_scope, .len = &fr_ldap_scope_len } },
90  { FR_CONF_OFFSET("attribute", rlm_ldap_t, profile_attr) },
91  { FR_CONF_OFFSET("attribute_suspend", rlm_ldap_t, profile_attr_suspend) },
93 };
94 
95 /*
96  * User configuration
97  */
99  { FR_CONF_OFFSET("scope", rlm_ldap_t, user.obj_scope), .dflt = "sub",
100  .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = fr_ldap_scope, .len = &fr_ldap_scope_len } },
101  { FR_CONF_OFFSET("sort_by", rlm_ldap_t, user.obj_sort_by) },
102 
103  { FR_CONF_OFFSET("access_attribute", rlm_ldap_t, user.obj_access_attr) },
104  { FR_CONF_OFFSET("access_positive", rlm_ldap_t, user.access_positive), .dflt = "yes" },
105  { FR_CONF_OFFSET("access_value_negate", rlm_ldap_t, user.access_value_negate), .dflt = "false" },
106  { FR_CONF_OFFSET("access_value_suspend", rlm_ldap_t, user.access_value_suspend), .dflt = "suspended" },
107  { FR_CONF_OFFSET_IS_SET("expect_password", FR_TYPE_BOOL, 0, rlm_ldap_t, user.expect_password) },
109 };
110 
111 /*
112  * Group configuration
113  */
115  { FR_CONF_OFFSET("filter", rlm_ldap_t, group.obj_filter) },
116  { FR_CONF_OFFSET("scope", rlm_ldap_t, group.obj_scope), .dflt = "sub",
117  .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = fr_ldap_scope, .len = &fr_ldap_scope_len } },
118 
119  { FR_CONF_OFFSET("name_attribute", rlm_ldap_t, group.obj_name_attr), .dflt = "cn" },
120  { FR_CONF_OFFSET("membership_attribute", rlm_ldap_t, group.userobj_membership_attr) },
121  { FR_CONF_OFFSET_FLAGS("membership_filter", CONF_FLAG_XLAT, rlm_ldap_t, group.obj_membership_filter) },
122  { FR_CONF_OFFSET("cacheable_name", rlm_ldap_t, group.cacheable_name), .dflt = "no" },
123  { FR_CONF_OFFSET("cacheable_dn", rlm_ldap_t, group.cacheable_dn), .dflt = "no" },
124  { FR_CONF_OFFSET("cache_attribute", rlm_ldap_t, group.cache_attribute) },
125  { FR_CONF_OFFSET("group_attribute", rlm_ldap_t, group.attribute) },
126  { FR_CONF_OFFSET("allow_dangling_group_ref", rlm_ldap_t, group.allow_dangling_refs), .dflt = "no" },
127  { FR_CONF_OFFSET("skip_on_suspend", rlm_ldap_t, group.skip_on_suspend), .dflt = "yes"},
129 };
130 
131 /*
132  * Reference for accounting updates
133  */
135  { FR_CONF_OFFSET_FLAGS("reference", CONF_FLAG_XLAT, ldap_acct_section_t, reference), .dflt = "." },
137 };
138 
139 static const conf_parser_t module_config[] = {
140  /*
141  * Pool config items
142  */
143  { FR_CONF_OFFSET_FLAGS("server", CONF_FLAG_MULTI, rlm_ldap_t, handle_config.server_str) }, /* Do not set to required */
144 
145  /*
146  * Common LDAP conf parsers
147  */
149 
150  { FR_CONF_OFFSET("valuepair_attribute", rlm_ldap_t, valuepair_attr) },
151 
152 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
153  { FR_CONF_OFFSET("session_tracking", rlm_ldap_t, session_tracking), .dflt = "no" },
154 #endif
155 
156 #ifdef WITH_EDIR
157  /* support for eDirectory Universal Password */
158  { FR_CONF_OFFSET("edir", rlm_ldap_t, edir) }, /* NULL defaults to "no" */
159 
160  /*
161  * Attempt to bind with the cleartext password we got from eDirectory
162  * Universal password for additional authorization checks.
163  */
164  { FR_CONF_OFFSET("edir_autz", rlm_ldap_t, edir_autz) }, /* NULL defaults to "no" */
165 #endif
166 
167  { FR_CONF_POINTER("user", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) user_config },
168 
169  { FR_CONF_POINTER("group", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) group_config },
170 
171  { FR_CONF_POINTER("profile", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) profile_config },
172 
173  { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_ldap_t, trunk_conf, fr_trunk_config ) },
174 
175  { FR_CONF_OFFSET_SUBSECTION("bind_pool", 0, rlm_ldap_t, bind_trunk_conf, fr_trunk_config ) },
176 
178 };
179 
180 #define USER_CALL_ENV_COMMON(_struct) \
181  { FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, _struct, user_base), .pair.dflt = "", .pair.dflt_quote = T_SINGLE_QUOTED_STRING }, \
182  { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, _struct, user_filter), .pair.dflt = "(&)", .pair.dflt_quote = T_SINGLE_QUOTED_STRING }
183 
186  .env = (call_env_parser_t[]) {
188  ((call_env_parser_t[]) {
190  { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING,
192  ldap_auth_call_env_t, password, password_tmpl),
193  .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD },
196  })) },
198  }
199 };
200 
201 /** Parameters to allow ldap_update_section_parse to be reused
202  */
203 typedef struct {
204  size_t map_offset;
207 
210  .env = (call_env_parser_t[]) {
212  .uctx = &(ldap_update_rules_t){
213  .map_offset = offsetof(ldap_autz_call_env_t, user_map),
214  .expect_password_offset = offsetof(ldap_autz_call_env_t, expect_password)
215  } },
217  ((call_env_parser_t[]) {
220  })) },
222  ((call_env_parser_t[]) {
225  .pair.func = ldap_group_filter_parse,
226  .pair.escape = {
227  .func = fr_ldap_box_escape,
229  .mode = TMPL_ESCAPE_PRE_CONCAT
230  },
231  .pair.literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
232  },
234  })) },
235  { FR_CALL_ENV_SUBSECTION("profile", NULL, CALL_ENV_FLAG_NONE,
236  ((call_env_parser_t[]) {
239  .pair.dflt = "(&)", .pair.dflt_quote = T_SINGLE_QUOTED_STRING }, //!< Correct filter for when the DN is known.
241  } )) },
243  }
244 };
245 
248  .env = (call_env_parser_t[]) {
250  ((call_env_parser_t[]) {
253  })) },
254 
256  }
257 };
258 
261  .env = (call_env_parser_t[]) {
263  ((call_env_parser_t[]) {
266  })) },
268  ((call_env_parser_t[]) {
271  .pair.func = ldap_group_filter_parse,
272  .pair.escape = {
273  .func = fr_ldap_box_escape,
275  .mode = TMPL_ESCAPE_PRE_CONCAT
276  },
277  .pair.literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
278  },
280  })) },
282  }
283 };
284 
287  .env = (call_env_parser_t[]) {
289  .uctx = &(ldap_update_rules_t){
290  .map_offset = offsetof(ldap_xlat_profile_call_env_t, profile_map),
291  .expect_password_offset = -1
292  } },
293  { FR_CALL_ENV_SUBSECTION("profile", NULL, CALL_ENV_FLAG_NONE,
294  ((call_env_parser_t[]) {
296  .pair.dflt = "(&)", .pair.dflt_quote = T_SINGLE_QUOTED_STRING }, //!< Correct filter for when the DN is known.
298  })) },
300  }
301 };
302 
304 
307  { .out = &dict_freeradius, .proto = "freeradius" },
308  { NULL }
309 };
310 
318 
321  { .out = &attr_password, .name = "Password", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
322  { .out = &attr_cleartext_password, .name = "Password.Cleartext", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
323  { .out = &attr_crypt_password, .name = "Password.Crypt", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
324  { .out = &attr_ldap_userdn, .name = "LDAP-UserDN", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
325  { .out = &attr_nt_password, .name = "Password.NT", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
326  { .out = &attr_password_with_header, .name = "Password.With-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
327  { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
328 
329  { NULL }
330 };
331 
332 extern global_lib_autoinst_t const *rlm_ldap_lib[];
336 };
337 
338 /** Holds state of in progress async authentication
339  *
340  */
341 typedef struct {
342  char const *dn;
343  char const *password;
344  rlm_ldap_t const *inst;
348 
349 /** Holds state of in progress ldap user modifications
350  *
351  */
352 typedef struct {
353  rlm_ldap_t const *inst;
355  char const *dn;
356  char *passed[LDAP_MAX_ATTRMAP * 2];
357  LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1];
358  LDAPMod mod_s[LDAP_MAX_ATTRMAP];
362 
363 /** Holds state of in progress LDAP map
364  *
365  */
366 typedef struct {
367  map_list_t const *maps;
368  LDAPURLDesc *ldap_url;
372 
373 typedef enum {
378 
380  { L("ldap://"), LDAP_SCHEME_UNIX },
381  { L("ldapi://"), LDAP_SCHEME_TCP },
382  { L("ldaps://"), LDAP_SCHEME_TCP_SSL },
383 };
385 
386 /** This is the common function that actually ends up doing all the URI escaping
387  */
388 #define LDAP_URI_SAFE_FOR (fr_value_box_safe_for_t)fr_ldap_uri_escape_func
389 
391  { .required = true, .concat = true, .type = FR_TYPE_STRING, .safe_for = LDAP_URI_SAFE_FOR },
393 };
394 
396  { .required = true, .concat = true, .type = FR_TYPE_STRING },
398 };
399 
400 /** Escape LDAP string
401  *
402  * @ingroup xlat_functions
403  */
405  UNUSED xlat_ctx_t const *xctx,
406  request_t *request, fr_value_box_list_t *in)
407 {
408  fr_value_box_t *vb, *in_vb = fr_value_box_list_head(in);
409  fr_sbuff_t sbuff;
410  fr_sbuff_uctx_talloc_t sbuff_ctx;
411  size_t len;
412 
413  MEM(vb = fr_value_box_alloc_null(ctx));
414 
415  /*
416  * If it's already safe, just copy it over.
417  */
419  fr_value_box_copy(vb, vb, in_vb);
420 
421  fr_dcursor_append(out, vb);
422  return XLAT_ACTION_DONE;
423  }
424 
425  /*
426  * Maximum space needed for output would be 3 times the input if every
427  * char needed escaping
428  */
429  if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, in_vb->vb_length * 3, in_vb->vb_length * 3)) {
430  REDEBUG("Failed to allocate buffer for escaped string");
431  talloc_free(vb);
432  return XLAT_ACTION_FAIL;
433  }
434 
435  /*
436  * Call the escape function, including the space for the trailing NULL
437  */
438  len = fr_ldap_uri_escape_func(request, fr_sbuff_buff(&sbuff), in_vb->vb_length * 3 + 1, in_vb->vb_strvalue, NULL);
439 
440  /*
441  * Trim buffer to fit used space and assign to box
442  */
443  fr_sbuff_trim_talloc(&sbuff, len);
444  fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), in_vb->tainted);
445 
446  fr_dcursor_append(out, vb);
447  return XLAT_ACTION_DONE;
448 }
449 
451  { .required = true, .concat = true, .type = FR_TYPE_STRING },
453 };
454 
455 /** Unescape LDAP string
456  *
457  * @ingroup xlat_functions
458  */
460  UNUSED xlat_ctx_t const *xctx,
461  request_t *request, fr_value_box_list_t *in)
462 {
463  fr_value_box_t *vb, *in_vb = fr_value_box_list_head(in);
464  fr_sbuff_t sbuff;
465  fr_sbuff_uctx_talloc_t sbuff_ctx;
466  size_t len;
467 
468  MEM(vb = fr_value_box_alloc_null(ctx));
469  /*
470  * Maximum space needed for output will be the same as the input
471  */
472  if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, in_vb->vb_length, in_vb->vb_length)) {
473  REDEBUG("Failed to allocate buffer for unescaped string");
474  talloc_free(vb);
475  return XLAT_ACTION_FAIL;
476  }
477 
478  /*
479  * Call the unescape function, including the space for the trailing NULL
480  */
481  len = fr_ldap_uri_unescape_func(request, fr_sbuff_buff(&sbuff), in_vb->vb_length + 1, in_vb->vb_strvalue, NULL);
482 
483  /*
484  * Trim buffer to fit used space and assign to box
485  */
486  fr_sbuff_trim_talloc(&sbuff, len);
487  fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), in_vb->tainted);
488  fr_dcursor_append(out, vb);
489 
490  return XLAT_ACTION_DONE;
491 }
492 
493 /** Escape function for a part of an LDAP URI
494  *
495  */
496 static int ldap_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
497 {
498  fr_sbuff_t sbuff;
499  fr_sbuff_uctx_talloc_t sbuff_ctx;
500  size_t len;
501 
502  /*
503  * Maximum space needed for output would be 3 times the input if every
504  * char needed escaping
505  */
506  if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->vb_length * 3, vb->vb_length * 3)) {
507  fr_strerror_printf_push("Failed to allocate buffer for escaped argument");
508  return -1;
509  }
510 
511  /*
512  * Call the escape function, including the space for the trailing NULL
513  */
514  len = fr_ldap_uri_escape_func(NULL, fr_sbuff_buff(&sbuff), vb->vb_length * 3 + 1, vb->vb_strvalue, NULL);
515 
516  fr_sbuff_trim_talloc(&sbuff, len);
518  fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), vb->tainted);
519 
520  return 0;
521 }
522 
523 /** Callback when LDAP query times out
524  *
525  */
527 {
528  fr_ldap_query_t *query = talloc_get_type_abort(uctx, fr_ldap_query_t);
529  fr_trunk_request_t *treq;
530  request_t *request;
531 
532  /*
533  * If the trunk request has completed but the query
534  * has not yet resumed, query->treq will be NULL
535  */
536  if (!query->treq) return;
537 
538  treq = talloc_get_type_abort(query->treq, fr_trunk_request_t);
539  request = treq->request;
540 
541  ROPTIONAL(RERROR, ERROR, "Timeout waiting for LDAP query");
542 
544 
545  query->ret = LDAP_RESULT_TIMEOUT;
547 }
548 
549 /** Callback when resuming after async ldap query is completed
550  *
551  */
553  xlat_ctx_t const *xctx,
554  request_t *request, UNUSED fr_value_box_list_t *in)
555 {
556  fr_ldap_query_t *query = talloc_get_type_abort(xctx->rctx, fr_ldap_query_t);
557  fr_ldap_connection_t *ldap_conn = query->ldap_conn;
558  fr_value_box_t *vb = NULL;
559  LDAPMessage *msg;
560  struct berval **values;
561  char const **attr;
562  int count, i;
563 
564  if (query->ret != LDAP_RESULT_SUCCESS) return XLAT_ACTION_FAIL;
565 
566  /*
567  * We only parse "entries"
568  */
569  for (msg = ldap_first_entry(ldap_conn->handle, query->result); msg; msg = ldap_next_entry(ldap_conn->handle, msg)) {
570  for (attr = query->search.attrs; *attr; attr++) {
571  values = ldap_get_values_len(ldap_conn->handle, msg, *attr);
572  if (!values) {
573  RDEBUG2("No \"%s\" attributes found in specified object", *attr);
574  continue;
575  }
576 
577  count = ldap_count_values_len(values);
578  for (i = 0; i < count; i++) {
579  MEM(vb = fr_value_box_alloc_null(ctx));
580  if (fr_value_box_bstrndup(vb, vb, NULL, values[i]->bv_val, values[i]->bv_len, true) < 0) {
581  talloc_free(vb);
582  RPERROR("Failed creating value from LDAP response");
583  break;
584  }
585  fr_dcursor_append(out, vb);
586  }
587  ldap_value_free_len(values);
588  }
589  }
590 
591  talloc_free(query);
592 
593  return XLAT_ACTION_DONE;
594 }
595 
596 /** Callback for signalling async ldap query
597  *
598  */
599 static void ldap_xlat_signal(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
600 {
601  fr_ldap_query_t *query = talloc_get_type_abort(xctx->rctx, fr_ldap_query_t);
602 
603  if (!query->treq) return;
604 
605  RDEBUG2("Forcefully cancelling pending LDAP query");
606 
608 }
609 
610 /*
611  * If a part doesn't have an escaping function, parsing will fail unless the input
612  * was marked up with a safe_for value by the ldap arg parsing, i.e. was a literal
613  * input argument to the xlat.
614  *
615  * This is equivalent to the old "tainted_allowed" flag.
616  */
617 static fr_uri_part_t const ldap_uri_parts[] = {
618  { .name = "scheme", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 }, .extra_skip = 2 },
619  { .name = "host", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 } },
620  { .name = "port", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 } },
621  { .name = "dn", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = ldap_uri_part_escape },
622  { .name = "attrs", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }},
623  { .name = "scope", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = ldap_uri_part_escape },
624  { .name = "filter", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1}, .func = ldap_uri_part_escape },
625  { .name = "exts", .safe_for = LDAP_URI_SAFE_FOR, .func = ldap_uri_part_escape },
627 };
628 
629 static fr_uri_part_t const ldap_dn_parts[] = {
630  { .name = "dn", .safe_for = LDAP_URI_SAFE_FOR , .func = ldap_uri_part_escape },
632 };
633 
635  { .required = true, .type = FR_TYPE_STRING, .safe_for = LDAP_URI_SAFE_FOR },
637 };
638 
639 /** Produce canonical LDAP host URI for finding trunks
640  *
641  */
642 static inline CC_HINT(always_inline)
643 char *host_uri_canonify(request_t *request, LDAPURLDesc *url_parsed, fr_value_box_t *url_in)
644 {
645  char *host;
646 
647  LDAPURLDesc tmp_desc = {
648  .lud_scheme = url_parsed->lud_scheme,
649  .lud_host = url_parsed->lud_host,
650  .lud_port = url_parsed->lud_port,
651  .lud_scope = -1
652  };
653  host = ldap_url_desc2str(&tmp_desc);
654  if (unlikely(host == NULL)) REDEBUG("Invalid LDAP URL - %pV", url_in); \
655 
656  return host;
657 }
658 
659 /** Expand an LDAP URL into a query, and return a string result from that query.
660  *
661  * @ingroup xlat_functions
662  */
664  xlat_ctx_t const *xctx,
665  request_t *request, fr_value_box_list_t *in)
666 {
667  fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
668  fr_value_box_t *uri_components, *uri;
669  char *host_url, *host = NULL;
670  fr_ldap_config_t const *handle_config = t->config;
671  fr_ldap_thread_trunk_t *ttrunk;
672  fr_ldap_query_t *query = NULL;
673 
674  LDAPURLDesc *ldap_url;
675  int ldap_url_ret;
676 
677  XLAT_ARGS(in, &uri_components);
678 
679  if (fr_uri_escape_list(&uri_components->vb_group, ldap_uri_parts, NULL) < 0){
680  RPERROR("Failed to escape LDAP URI");
681  return XLAT_ACTION_FAIL;
682  }
683 
684  /*
685  * Smush everything into the first URI box
686  */
687  uri = fr_value_box_list_head(&uri_components->vb_group);
688 
689  if (fr_value_box_list_concat_in_place(uri, uri, &uri_components->vb_group,
690  FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
691  REDEBUG("Failed concattenating input");
692  return XLAT_ACTION_FAIL;
693  }
694 
695  if (!ldap_is_ldap_url(uri->vb_strvalue)) {
696  REDEBUG("String passed does not look like an LDAP URL");
697  return XLAT_ACTION_FAIL;
698  }
699 
700  ldap_url_ret = ldap_url_parse(uri->vb_strvalue, &ldap_url);
701  if (ldap_url_ret != LDAP_URL_SUCCESS){
702  RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
703  error:
704  ldap_free_urldesc(ldap_url);
705  return XLAT_ACTION_FAIL;
706  }
707 
708  /*
709  * Nothing, empty string, "*" string, or got 2 things, die.
710  */
711  if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] || !*ldap_url->lud_attrs[0] ||
712  (strcmp(ldap_url->lud_attrs[0], "*") == 0) || ldap_url->lud_attrs[1]) {
713  REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
714  goto error;
715  }
716 
718  ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
719  (char const * const*)ldap_url->lud_attrs, NULL, NULL);
720  query->ldap_url = ldap_url; /* query destructor will free URL */
721 
722  if (ldap_url->lud_exts) {
723  LDAPControl *serverctrls[LDAP_MAX_CONTROLS];
724  int i;
725 
726  if (fr_ldap_parse_url_extensions(serverctrls, NUM_ELEMENTS(serverctrls),
727  query->ldap_url->lud_exts) < 0) {
728  RPERROR("Parsing URL extensions failed");
729  query_error:
730  talloc_free(query);
731  return XLAT_ACTION_FAIL;
732  }
733 
734  for (i = 0; i < LDAP_MAX_CONTROLS; i++) {
735  if (!serverctrls[i]) break;
736  query->serverctrls[i].control = serverctrls[i];
737  query->serverctrls[i].freeit = true;
738  }
739  }
740 
741  /*
742  * If the URL is <scheme>:/// the parsed host will be NULL - use config default
743  */
744  if (!ldap_url->lud_host) {
745  host_url = handle_config->server;
746  } else {
747  host_url = host = host_uri_canonify(request, ldap_url, uri);
748  if (unlikely(host_url == NULL)) goto query_error;
749  }
750 
751  ttrunk = fr_thread_ldap_trunk_get(t, host_url, handle_config->admin_identity,
752  handle_config->admin_password, request, handle_config);
753  if (host) ldap_memfree(host);
754  if (!ttrunk) {
755  REDEBUG("Unable to get LDAP query for xlat");
756  goto query_error;
757  }
758 
759  switch (fr_trunk_request_enqueue(&query->treq, ttrunk->trunk, request, query, NULL)) {
760  case FR_TRUNK_ENQUEUE_OK:
762  break;
763 
764  default:
765  REDEBUG("Unable to enqueue LDAP query for xlat");
766  goto query_error;
767  }
768 
769  if (fr_event_timer_in(query, unlang_interpret_event_list(request), &query->ev, handle_config->res_timeout,
770  ldap_query_timeout, query) < 0) {
771  REDEBUG("Unable to set timeout for LDAP query");
773  goto query_error;
774  }
775 
776  return unlang_xlat_yield(request, ldap_xlat_resume, ldap_xlat_signal, ~FR_SIGNAL_CANCEL, query);
777 }
778 
779 /** User object lookup as part of group membership xlat
780  *
781  * Called if the ldap membership xlat is used and the user DN is not already known
782  */
784  request_t *request, void *uctx)
785 {
786  ldap_memberof_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(uctx, ldap_memberof_xlat_ctx_t);
787 
788  if (xlat_ctx->env_data->user_filter.type == FR_TYPE_STRING) xlat_ctx->filter = &xlat_ctx->env_data->user_filter;
789 
790  xlat_ctx->basedn = &xlat_ctx->env_data->user_base;
791 
792  return rlm_ldap_find_user_async(xlat_ctx, xlat_ctx->inst, request, xlat_ctx->basedn, xlat_ctx->filter,
793  xlat_ctx->ttrunk, xlat_ctx->attrs, &xlat_ctx->query);
794 }
795 
796 /** Cancel an in-progress query for the LDAP group membership xlat
797  *
798  */
799 static void ldap_memberof_xlat_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
800 {
801  ldap_memberof_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(uctx, ldap_memberof_xlat_ctx_t);
802 
803  if (!xlat_ctx->query || !xlat_ctx->query->treq) return;
804 
806 }
807 
808 #define REPEAT_LDAP_MEMBEROF_XLAT_RESULTS \
809  if (unlang_function_repeat_set(request, ldap_memberof_xlat_results) < 0) do { \
810  rcode = RLM_MODULE_FAIL; \
811  goto finish; \
812  } while (0)
813 
814 /** Run the state machine for the LDAP membership xlat
815  *
816  * This is called after each async lookup is completed
817  */
819  request_t *request, void *uctx)
820 {
821  ldap_memberof_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(uctx, ldap_memberof_xlat_ctx_t);
822  rlm_ldap_t const *inst = xlat_ctx->inst;
824 
825  switch (xlat_ctx->status) {
827  if (!xlat_ctx->dn) xlat_ctx->dn = rlm_find_user_dn_cached(request);
828  if (!xlat_ctx->dn) RETURN_MODULE_FAIL;
829 
830  if (inst->group.obj_membership_filter) {
832  if (rlm_ldap_check_groupobj_dynamic(&rcode, request, xlat_ctx) == UNLANG_ACTION_PUSHED_CHILD) {
833  xlat_ctx->status = GROUP_XLAT_MEMB_FILTER;
835  }
836  }
837  FALL_THROUGH;
838 
840  if (xlat_ctx->found) {
841  rcode = RLM_MODULE_OK;
842  goto finish;
843  }
844 
845  if (inst->group.userobj_membership_attr) {
847  if (rlm_ldap_check_userobj_dynamic(&rcode, request, xlat_ctx) == UNLANG_ACTION_PUSHED_CHILD) {
848  xlat_ctx->status = GROUP_XLAT_MEMB_ATTR;
850  }
851  }
852  FALL_THROUGH;
853 
855  if (xlat_ctx->found) rcode = RLM_MODULE_OK;
856  break;
857  }
858 
859 finish:
860  RETURN_MODULE_RCODE(rcode);
861 }
862 
863 /** Process the results of evaluating LDAP group membership
864  *
865  */
866 static xlat_action_t ldap_memberof_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
867  UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
868 {
869  ldap_memberof_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, ldap_memberof_xlat_ctx_t);
870  fr_value_box_t *vb;
871 
873  vb->vb_bool = xlat_ctx->found;
874  fr_dcursor_append(out, vb);
875 
876  return XLAT_ACTION_DONE;
877 }
878 
880  { .required = true, .concat = true, .type = FR_TYPE_STRING, .safe_for = LDAP_URI_SAFE_FOR },
882 };
883 
884 /** Check for a user being in a LDAP group
885  *
886  * @ingroup xlat_functions
887  */
888 static xlat_action_t ldap_memberof_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
889  request_t *request, fr_value_box_list_t *in)
890 {
891  fr_value_box_t *vb = NULL, *group_vb = fr_value_box_list_pop_head(in);
893  fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
894  ldap_xlat_memberof_call_env_t *env_data = talloc_get_type_abort(xctx->env_data, ldap_xlat_memberof_call_env_t);
895  bool group_is_dn;
896  ldap_memberof_xlat_ctx_t *xlat_ctx;
897 
898  RDEBUG2("Searching for user in group \"%pV\"", group_vb);
899 
900  if (group_vb->vb_length == 0) {
901  REDEBUG("Cannot do comparison (group name is empty)");
902  return XLAT_ACTION_FAIL;
903  }
904 
905  group_is_dn = fr_ldap_util_is_dn(group_vb->vb_strvalue, group_vb->vb_length);
906  if (group_is_dn) {
907  char *norm;
908  size_t len;
909 
910  MEM(norm = talloc_array(group_vb, char, talloc_array_length(group_vb->vb_strvalue)));
911  len = fr_ldap_util_normalise_dn(norm, group_vb->vb_strvalue);
912 
913  /*
914  * Will clear existing buffer (i.e. group_vb->vb_strvalue)
915  */
916  fr_value_box_bstrdup_buffer_shallow(group_vb, group_vb, NULL, norm, group_vb->tainted);
917 
918  /*
919  * Trim buffer to match normalised DN
920  */
921  fr_value_box_bstr_realloc(group_vb, NULL, group_vb, len);
922  }
923 
924  if ((group_is_dn && inst->group.cacheable_dn) || (!group_is_dn && inst->group.cacheable_name)) {
925  rlm_rcode_t our_rcode;
926 
927  rlm_ldap_check_cached(&our_rcode, inst, request, group_vb);
928  switch (our_rcode) {
929  case RLM_MODULE_NOTFOUND:
930  RDEBUG2("User is not a member of \"%pV\"", group_vb);
931  return XLAT_ACTION_DONE;
932 
933  case RLM_MODULE_OK:
934  MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
935  vb->vb_bool = true;
936  fr_dcursor_append(out, vb);
937  return XLAT_ACTION_DONE;
938 
939  /*
940  * Fallback to dynamic search
941  */
942  default:
943  break;
944  }
945  }
946 
947  MEM(xlat_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_memberof_xlat_ctx_t));
948 
949  *xlat_ctx = (ldap_memberof_xlat_ctx_t){
950  .inst = inst,
951  .group = group_vb,
952  .dn = rlm_find_user_dn_cached(request),
953  .attrs = { inst->group.userobj_membership_attr, NULL },
954  .group_is_dn = group_is_dn,
955  .env_data = env_data
956  };
957 
958  xlat_ctx->ttrunk = fr_thread_ldap_trunk_get(t, inst->handle_config.server, inst->handle_config.admin_identity,
959  inst->handle_config.admin_password, request, &inst->handle_config);
960 
961  if (!xlat_ctx->ttrunk) {
962  REDEBUG("Unable to get LDAP trunk for group membership check");
963  error:
964  talloc_free(xlat_ctx);
965  return XLAT_ACTION_FAIL;
966  }
967 
968  if (unlang_xlat_yield(request, ldap_memberof_xlat_resume, NULL, 0, xlat_ctx) != XLAT_ACTION_YIELD) goto error;
969 
970  if (unlang_function_push(request, xlat_ctx->dn ? NULL : ldap_memberof_xlat_user_find,
972  UNLANG_SUB_FRAME, xlat_ctx) < 0) goto error;
973 
975 }
976 
977 typedef struct {
979  LDAPURLDesc *url;
982 
983 /** Return whether evaluating the profile was successful
984  *
985  */
986 static xlat_action_t ldap_profile_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
987  UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
988 {
989  ldap_xlat_profile_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, ldap_xlat_profile_ctx_t);
990  fr_value_box_t *vb;
991 
993  vb->vb_bool = xlat_ctx->ret == LDAP_RESULT_SUCCESS;
994  fr_dcursor_append(out, vb);
995 
996  return XLAT_ACTION_DONE;
997 }
998 
1000 {
1001  if (to_free->url) {
1002  ldap_free_urldesc(to_free->url);
1003  to_free->url = NULL;
1004  }
1005  return 0;
1006 }
1007 
1008 /** Expand an LDAP URL into a query, applying the results using the user update map.
1009  *
1010  * For fetching profiles by DN.
1011  *
1012  * @ingroup xlat_functions
1013  */
1015  xlat_ctx_t const *xctx,
1016  request_t *request, fr_value_box_list_t *in)
1017 {
1019  fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
1020  ldap_xlat_profile_call_env_t *env_data = talloc_get_type_abort(xctx->env_data, ldap_xlat_profile_call_env_t);
1021  fr_value_box_t *uri_components, *uri;
1022  char *host_url, *host = NULL;
1023  fr_ldap_config_t const *handle_config = t->config;
1024  fr_ldap_thread_trunk_t *ttrunk;
1025  ldap_xlat_profile_ctx_t *xlat_ctx = NULL;
1026 
1027  int ldap_url_ret;
1028 
1029  char const *dn;
1030  char const *filter;
1031  int scope;
1032 
1033  bool is_dn;
1034 
1035  XLAT_ARGS(in, &uri_components);
1036 
1037  is_dn = (fr_uri_has_scheme(&uri_components->vb_group, ldap_uri_scheme_table, ldap_uri_scheme_table_len, -1) < 0);
1038 
1039  /*
1040  * Apply different escaping rules based on whether the first
1041  * arg lookgs like a URI or a DN.
1042  */
1043  if (is_dn) {
1044  if (fr_uri_escape_list(&uri_components->vb_group, ldap_dn_parts, NULL) < 0) {
1045  RPERROR("Failed to escape LDAP DN");
1046  return XLAT_ACTION_FAIL;
1047  }
1048  } else {
1049  if (fr_uri_escape_list(&uri_components->vb_group, ldap_uri_parts, NULL) < 0) {
1050  RPERROR("Failed to escape LDAP URI");
1051  return XLAT_ACTION_FAIL;
1052  }
1053  }
1054 
1055  /*
1056  * Smush everything into the first URI box
1057  */
1058  uri = fr_value_box_list_head(&uri_components->vb_group);
1059  if (fr_value_box_list_concat_in_place(uri, uri, &uri_components->vb_group,
1060  FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
1061  REDEBUG("Failed concattenating input");
1062  return XLAT_ACTION_FAIL;
1063  }
1064 
1065  /*
1066  * Allocate a resumption context to store temporary resource and results
1067  */
1068  MEM(xlat_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_xlat_profile_ctx_t));
1069  talloc_set_destructor(xlat_ctx, ldap_xlat_profile_ctx_free);
1070 
1071  if (is_dn) {
1072  host_url = handle_config->server;
1073  dn = talloc_typed_strdup_buffer(xlat_ctx, uri->vb_strvalue);
1074  filter = env_data->profile_filter.vb_strvalue;
1075  scope = inst->profile_scope;
1076  } else {
1077  ldap_url_ret = ldap_url_parse(uri->vb_strvalue, &xlat_ctx->url);
1078  if (ldap_url_ret != LDAP_URL_SUCCESS){
1079  RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
1080  error:
1081  talloc_free(xlat_ctx);
1082  return XLAT_ACTION_FAIL;
1083  }
1084 
1085  /*
1086  * The URL must specify a DN
1087  */
1088  if (!xlat_ctx->url->lud_dn) {
1089  REDEBUG("LDAP URI must specify a profile DN");
1090  goto error;
1091  }
1092 
1093  dn = xlat_ctx->url->lud_dn;
1094  /*
1095  * Either we use the filter from the URL or we use the default filter
1096  * configured for profiles.
1097  */
1098  filter = xlat_ctx->url->lud_filter ? xlat_ctx->url->lud_filter : env_data->profile_filter.vb_strvalue;
1099 
1100  /*
1101  * Determine if the URL includes a scope.
1102  */
1103  scope = xlat_ctx->url->lud_scope == LDAP_SCOPE_DEFAULT ? inst->profile_scope : xlat_ctx->url->lud_scope;
1104 
1105  /*
1106  * If the URL is <scheme>:/// the parsed host will be NULL - use config default
1107  */
1108  if (!xlat_ctx->url->lud_host) {
1109  host_url = handle_config->server;
1110  } else {
1111  host_url = host = host_uri_canonify(request, xlat_ctx->url, uri);
1112  if (unlikely(host_url == NULL)) goto error;
1113  }
1114  }
1115 
1116  /*
1117  * Synchronous expansion of maps (fixme!)
1118  */
1119  if (fr_ldap_map_expand(xlat_ctx, &xlat_ctx->expanded, request, env_data->profile_map,
1120  inst->valuepair_attr) < 0) goto error;
1121  ttrunk = fr_thread_ldap_trunk_get(t, host_url, handle_config->admin_identity,
1122  handle_config->admin_password, request, handle_config);
1123  if (host) ldap_memfree(host);
1124  if (!ttrunk) {
1125  REDEBUG("Unable to get LDAP query for xlat");
1126  goto error;
1127  }
1128 
1129  if (unlang_xlat_yield(request, ldap_profile_xlat_resume, NULL, 0, xlat_ctx) != XLAT_ACTION_YIELD) goto error;
1130 
1131  /*
1132  * Pushes a frame onto the stack to retrieve and evaluate a profile
1133  */
1134  if (rlm_ldap_map_profile(&xlat_ctx->ret, inst, request, ttrunk, dn, scope, filter, &xlat_ctx->expanded) < 0) goto error;
1135 
1136  return XLAT_ACTION_PUSH_UNLANG;
1137 }
1138 
1139 /*
1140  * Verify the result of the map.
1141  */
1142 static int ldap_map_verify(CONF_SECTION *cs, UNUSED void *mod_inst, UNUSED void *proc_inst,
1143  tmpl_t const *src, UNUSED map_list_t const *maps)
1144 {
1145  if (!src) {
1146  cf_log_err(cs, "Missing LDAP URI");
1147 
1148  return -1;
1149  }
1150 
1151  return 0;
1152 }
1153 
1154 /** Process the results of an LDAP map query
1155  *
1156  * @param[out] p_result Result of applying the map.
1157  * @param[in] priority Unused.
1158  * @param[in] request Current request.
1159  * @param[in] uctx Map context.
1160  * @return One of UNLANG_ACTION_*
1161  */
1162 static unlang_action_t mod_map_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1163 {
1164  ldap_map_ctx_t *map_ctx = talloc_get_type_abort(uctx, ldap_map_ctx_t);
1165  fr_ldap_query_t *query = map_ctx->query;
1166  fr_ldap_map_exp_t *expanded = &map_ctx->expanded;
1168  LDAPMessage *entry;
1169  map_t const *map;
1170 
1171  switch (query->ret) {
1172  case LDAP_RESULT_SUCCESS:
1173  rcode = RLM_MODULE_UPDATED;
1174  break;
1175 
1176  case LDAP_RESULT_NO_RESULT:
1177  case LDAP_RESULT_BAD_DN:
1178  goto finish;
1179 
1180  default:
1181  rcode = RLM_MODULE_FAIL;
1182  goto finish;
1183  }
1184 
1185  for (entry = ldap_first_entry(query->ldap_conn->handle, query->result);
1186  entry;
1187  entry = ldap_next_entry(query->ldap_conn->handle, entry)) {
1188  char *dn = NULL;
1189  int i;
1190 
1191  if (RDEBUG_ENABLED2) {
1192  dn = ldap_get_dn(query->ldap_conn->handle, entry);
1193  RDEBUG2("Processing \"%s\"", dn);
1194  }
1195 
1196  RINDENT();
1197  for (map = map_list_head(map_ctx->maps), i = 0;
1198  map != NULL;
1199  map = map_list_next(map_ctx->maps, map), i++) {
1200  int ret;
1201  fr_ldap_result_t attr;
1202 
1203  attr.values = ldap_get_values_len(query->ldap_conn->handle, entry, expanded->attrs[i]);
1204  if (!attr.values) {
1205  /*
1206  * Many LDAP directories don't expose the DN of
1207  * the object as an attribute, so we need this
1208  * hack, to allow the user to retrieve it.
1209  */
1210  if (strcmp(LDAP_VIRTUAL_DN_ATTR, expanded->attrs[i]) == 0) {
1211  struct berval value;
1212  struct berval *values[2] = { &value, NULL };
1213 
1214  if (!dn) dn = ldap_get_dn(query->ldap_conn->handle, entry);
1215  value.bv_val = dn;
1216  value.bv_len = strlen(dn);
1217 
1218  attr.values = values;
1219  attr.count = 1;
1220 
1221  ret = map_to_request(request, map, fr_ldap_map_getvalue, &attr);
1222  if (ret == -1) {
1223  rcode = RLM_MODULE_FAIL;
1224  ldap_memfree(dn);
1225  goto finish;
1226  }
1227  continue;
1228  }
1229 
1230  RDEBUG3("Attribute \"%s\" not found in LDAP object", expanded->attrs[i]);
1231 
1232  continue;
1233  }
1234  attr.count = ldap_count_values_len(attr.values);
1235 
1236  ret = map_to_request(request, map, fr_ldap_map_getvalue, &attr);
1237  ldap_value_free_len(attr.values);
1238  if (ret == -1) {
1239  rcode = RLM_MODULE_FAIL;
1240  ldap_memfree(dn);
1241  goto finish;
1242  }
1243  }
1244  ldap_memfree(dn);
1245  REXDENT();
1246  }
1247 
1248 finish:
1249  RETURN_MODULE_RCODE(rcode);
1250 }
1251 
1252 /** Ensure map context is properly cleared up
1253  *
1254  */
1255 static int map_ctx_free(ldap_map_ctx_t *map_ctx)
1256 {
1257  talloc_free(map_ctx->expanded.ctx);
1258  ldap_free_urldesc(map_ctx->ldap_url);
1259  return (0);
1260 }
1261 
1262 /** Perform a search and map the result of the search to server attributes
1263  *
1264  * Unlike LDAP xlat, this can be used to process attributes from multiple entries.
1265  *
1266  * @todo For xlat expansions we need to parse the raw URL first, and then apply
1267  * different escape functions to the different parts.
1268  *
1269  * @param[out] p_result Result of map expansion:
1270  * - #RLM_MODULE_NOOP no rows were returned.
1271  * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
1272  * - #RLM_MODULE_FAIL if an error occurred.
1273  * @param[in] mod_inst #rlm_ldap_t
1274  * @param[in] proc_inst unused.
1275  * @param[in,out] request The current request.
1276  * @param[in] url LDAP url specifying base DN and filter.
1277  * @param[in] maps Head of the map list.
1278  * @return UNLANG_ACTION_CALCULATE_RESULT
1279  */
1280 static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void *mod_inst, UNUSED void *proc_inst, request_t *request,
1281  fr_value_box_list_t *url, map_list_t const *maps)
1282 {
1283  rlm_ldap_t *inst = talloc_get_type_abort(mod_inst, rlm_ldap_t);
1284  fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
1285 
1286  LDAPURLDesc *ldap_url;
1287  int ldap_url_ret;
1288  fr_ldap_thread_trunk_t *ttrunk;
1289 
1290  fr_value_box_t *url_head;
1291  ldap_map_ctx_t *map_ctx;
1292  char *host_url, *host = NULL;
1293 
1294  /* FIXME - We need a way of markup up literal */
1295 /*
1296  * if (fr_uri_escape_list(url, ldap_uri_parts, NULL) < 0) {
1297  * RPERROR("Failed to escape LDAP URI");
1298  * RETURN_MODULE_FAIL;
1299  * }
1300  */
1301  url_head = fr_value_box_list_head(url);
1302  if (!url_head) {
1303  REDEBUG("LDAP URL cannot be (null)");
1305  }
1306 
1307  if (fr_value_box_list_concat_in_place(url_head, url_head, url, FR_TYPE_STRING,
1308  FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
1309  REDEBUG("Failed concatenating input");
1311  }
1312 
1313  if (!ldap_is_ldap_url(url_head->vb_strvalue)) {
1314  REDEBUG("Map query string does not look like a valid LDAP URI");
1316  }
1317 
1318  MEM(map_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_map_ctx_t));
1319  talloc_set_destructor(map_ctx, map_ctx_free);
1320  map_ctx->maps = maps;
1321 
1322  ldap_url_ret = ldap_url_parse(url_head->vb_strvalue, &map_ctx->ldap_url);
1323  if (ldap_url_ret != LDAP_URL_SUCCESS){
1324  RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
1325  fail:
1326  talloc_free(map_ctx);
1328  }
1329  ldap_url = map_ctx->ldap_url;
1330 
1331  /*
1332  * Expand the RHS of the maps to get the name of the attributes.
1333  */
1334  if (fr_ldap_map_expand(map_ctx, &map_ctx->expanded, request, maps, NULL) < 0) goto fail;
1335 
1336  /*
1337  * If the URL is <scheme>:/// the parsed host will be NULL - use config default
1338  */
1339  if (!ldap_url->lud_host) {
1340  host_url = inst->handle_config.server;
1341  } else {
1342  host_url = host = host_uri_canonify(request, ldap_url, url_head);
1343  if (unlikely(host_url == NULL)) goto fail;
1344  }
1345 
1346  ttrunk = fr_thread_ldap_trunk_get(thread, host_url, inst->handle_config.admin_identity,
1347  inst->handle_config.admin_password, request, &inst->handle_config);
1348  if (host) ldap_memfree(host);
1349  if (!ttrunk) goto fail;
1350 
1351  if (unlang_function_push(request, NULL, mod_map_resume, NULL, 0,
1352  UNLANG_SUB_FRAME, map_ctx) != UNLANG_ACTION_PUSHED_CHILD) goto fail;
1353 
1354  return fr_ldap_trunk_search(map_ctx, &map_ctx->query, request, ttrunk, ldap_url->lud_dn,
1355  ldap_url->lud_scope, ldap_url->lud_filter, map_ctx->expanded.attrs,
1356  NULL, NULL);
1357 }
1358 
1359 /** Perform async lookup of user DN if required for authentication
1360  *
1361  */
1363  request_t *request, void *uctx)
1364 {
1365  ldap_auth_ctx_t *auth_ctx = talloc_get_type_abort(uctx, ldap_auth_ctx_t);
1366  fr_ldap_thread_trunk_t *ttrunk;
1367  rlm_ldap_t const *inst = auth_ctx->inst;
1368 
1369  ttrunk = fr_thread_ldap_trunk_get(auth_ctx->thread, inst->handle_config.server, inst->handle_config.admin_identity,
1370  inst->handle_config.admin_password, request, &inst->handle_config);
1371  if (!ttrunk) RETURN_MODULE_FAIL;
1372 
1373  return rlm_ldap_find_user_async(auth_ctx, auth_ctx->inst, request, &auth_ctx->call_env->user_base,
1374  &auth_ctx->call_env->user_filter, ttrunk, NULL, NULL);
1375 }
1376 
1377 /** Initiate async LDAP bind to authenticate user
1378  *
1379  */
1381  request_t *request, void *uctx)
1382 {
1383  ldap_auth_ctx_t *auth_ctx = talloc_get_type_abort(uctx, ldap_auth_ctx_t);
1384 
1385  /*
1386  * SASL bind auth will have the mech set.
1387  */
1388  if (auth_ctx->call_env->user_sasl_mech.type == FR_TYPE_STRING) {
1389 #ifdef WITH_SASL
1390  ldap_auth_call_env_t *call_env = auth_ctx->call_env;
1391 
1392  RDEBUG2("Login attempt using identity \"%pV\"", &call_env->user_sasl_authname);
1393 
1394  return fr_ldap_sasl_bind_auth_async(request, auth_ctx->thread, call_env->user_sasl_mech.vb_strvalue,
1395  call_env->user_sasl_authname.vb_strvalue,
1396  auth_ctx->password, call_env->user_sasl_proxy.vb_strvalue,
1397  call_env->user_sasl_realm.vb_strvalue);
1398 #else
1399  RDEBUG("Configuration item 'sasl.mech' is not supported. "
1400  "The linked version of libldap does not provide ldap_sasl_bind( function");
1402 #endif
1403  }
1404 
1405  /*
1406  * Arriving here from an LDAP search will mean the dn in auth_ctx is NULL.
1407  */
1408  if (!auth_ctx->dn) auth_ctx->dn = rlm_find_user_dn_cached(request);
1409 
1410  /*
1411  * No DN found - can't authenticate the user with a simple bind.
1412  */
1413  if (!auth_ctx->dn) {
1414  talloc_free(auth_ctx);
1416  }
1417 
1418  RDEBUG2("Login attempt as \"%s\"", auth_ctx->dn);
1419 
1420  return fr_ldap_bind_auth_async(request, auth_ctx->thread, auth_ctx->dn, auth_ctx->password);
1421 }
1422 
1423 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1424 {
1426  fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
1427  ldap_auth_ctx_t *auth_ctx;
1428  ldap_auth_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_auth_call_env_t);
1429 
1430  if (call_env->password.type != FR_TYPE_STRING) {
1431  RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
1432  RWDEBUG("without checking if %s is present", call_env->password_tmpl->name);
1433  RWDEBUG("*********************************************");
1434  RWDEBUG("* THAT CONFIGURATION IS WRONG. DELETE IT. ");
1435  RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
1436  RWDEBUG("*********************************************");
1437 
1438  REDEBUG("Attribute \"%s\" is required for authentication", call_env->password_tmpl->name);
1440  }
1441 
1442  /*
1443  * Log the password
1444  */
1445  if (RDEBUG_ENABLED3) {
1446  RDEBUG("Login attempt with password \"%pV\"", &call_env->password);
1447  } else {
1448  RDEBUG2("Login attempt with password");
1449  }
1450 
1451  auth_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_auth_ctx_t);
1452  *auth_ctx = (ldap_auth_ctx_t){
1453  .password = call_env->password.vb_strvalue,
1454  .thread = thread,
1455  .inst = inst,
1456  .call_env = call_env
1457  };
1458 
1459  /*
1460  * Check for a cached copy of the DN
1461  */
1462  auth_ctx->dn = rlm_find_user_dn_cached(request);
1463 
1464  if (unlang_function_push(request, auth_ctx->dn || (call_env->user_sasl_mech.type == FR_TYPE_STRING) ?
1466  NULL, 0, UNLANG_SUB_FRAME, auth_ctx) < 0) RETURN_MODULE_FAIL;
1467 
1469 }
1470 
1471 /** Start LDAP authorization with async lookup of user DN
1472  *
1473  */
1475  request_t *request, void *uctx)
1476 {
1477  ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, ldap_autz_ctx_t);
1478  return rlm_ldap_find_user_async(autz_ctx, autz_ctx->inst, request, &autz_ctx->call_env->user_base,
1479  &autz_ctx->call_env->user_filter, autz_ctx->ttrunk, autz_ctx->expanded.attrs,
1480  &autz_ctx->query);
1481 }
1482 
1483 #define REPEAT_MOD_AUTHORIZE_RESUME \
1484  if (unlang_function_repeat_set(request, mod_authorize_resume) < 0) do { \
1485  rcode = RLM_MODULE_FAIL; \
1486  goto finish; \
1487  } while (0)
1488 
1489 /** Resume function called after each potential yield in LDAP authorization
1490  *
1491  * Some operations may or may not yield. E.g. if group membership is
1492  * read from an attribute returned with the user object and is already
1493  * in the correct form, that will not yield.
1494  * Hence, each state may fall through to the next.
1495  *
1496  * @param p_result Result of current authorization.
1497  * @param priority Unused.
1498  * @param request Current request.
1499  * @param uctx Current authorization context.
1500  * @return One of the RLM_MODULE_* values.
1501  */
1502 static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
1503 {
1504  ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, ldap_autz_ctx_t);
1506  ldap_autz_call_env_t *call_env = talloc_get_type_abort(autz_ctx->call_env, ldap_autz_call_env_t);
1507  int ldap_errno;
1508  rlm_rcode_t rcode = RLM_MODULE_OK;
1509  LDAP *handle = fr_ldap_handle_thread_local();
1510 
1511  /*
1512  * If a previous async call returned one of the "failure" results just return.
1513  */
1514  switch (*p_result) {
1515  case RLM_MODULE_REJECT:
1516  case RLM_MODULE_FAIL:
1517  case RLM_MODULE_HANDLED:
1518  case RLM_MODULE_INVALID:
1519  case RLM_MODULE_DISALLOW:
1520  rcode = *p_result;
1521  goto finish;
1522 
1523  default:
1524  break;
1525  }
1526 
1527  switch (autz_ctx->status) {
1528  case LDAP_AUTZ_FIND:
1529  /*
1530  * If a user entry has been found the current rcode will be OK
1531  */
1532  if (*p_result != RLM_MODULE_OK) return UNLANG_ACTION_CALCULATE_RESULT;
1533 
1534  autz_ctx->entry = ldap_first_entry(handle, autz_ctx->query->result);
1535  if (!autz_ctx->entry) {
1536  ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1537  REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1538 
1539  goto finish;
1540  }
1541 
1542  /*
1543  * Check for access.
1544  */
1545  if (inst->user.obj_access_attr) {
1546  autz_ctx->access_state = rlm_ldap_check_access(inst, request, autz_ctx->entry);
1547  switch (autz_ctx->access_state) {
1548  case LDAP_ACCESS_ALLOWED:
1549  break;
1550 
1551  case LDAP_ACCESS_SUSPENDED:
1552  if (inst->group.skip_on_suspend) goto post_group;
1553  break;
1554 
1556  rcode = RLM_MODULE_DISALLOW;
1557  goto finish;
1558  }
1559  }
1560 
1561  /*
1562  * Check if we need to cache group memberships
1563  */
1564  if ((inst->group.cacheable_dn || inst->group.cacheable_name) && (inst->group.userobj_membership_attr)) {
1566  if (rlm_ldap_cacheable_userobj(&rcode, request, autz_ctx,
1567  inst->group.userobj_membership_attr) == UNLANG_ACTION_PUSHED_CHILD) {
1568  autz_ctx->status = LDAP_AUTZ_GROUP;
1570  }
1571  if (rcode != RLM_MODULE_OK) goto finish;
1572  }
1573  FALL_THROUGH;
1574 
1575  case LDAP_AUTZ_GROUP:
1576  if (inst->group.cacheable_dn || inst->group.cacheable_name) {
1578  if (rlm_ldap_cacheable_groupobj(&rcode, request, autz_ctx) == UNLANG_ACTION_PUSHED_CHILD) {
1579  autz_ctx->status = LDAP_AUTZ_POST_GROUP;
1581  }
1582  if (rcode != RLM_MODULE_OK) goto finish;
1583  }
1584  FALL_THROUGH;
1585 
1586  case LDAP_AUTZ_POST_GROUP:
1587  post_group:
1588 #ifdef WITH_EDIR
1589  /*
1590  * We already have a Password.Cleartext. Skip edir.
1591  */
1592  if (fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_cleartext_password)) goto skip_edir;
1593 
1594  /*
1595  * Retrieve Universal Password if we use eDirectory
1596  */
1597  if (inst->edir) {
1598  autz_ctx->dn = rlm_find_user_dn_cached(request);
1599 
1600  /*
1601  * Retrieve universal password
1602  */
1604  autz_ctx->status = LDAP_AUTZ_EDIR_BIND;
1605  return fr_ldap_edir_get_password(request, autz_ctx->dn, autz_ctx->ttrunk,
1607  }
1608  FALL_THROUGH;
1609 
1610  case LDAP_AUTZ_EDIR_BIND:
1611  if (inst->edir && inst->edir_autz) {
1612  fr_pair_t *password = fr_pair_find_by_da(&request->control_pairs,
1613  NULL, attr_cleartext_password);
1614  fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data,
1616 
1617  if (!password) {
1618  REDEBUG("Failed to find &control.Password.Cleartext");
1619  rcode = RLM_MODULE_FAIL;
1620  goto finish;
1621  }
1622 
1623  RDEBUG2("Binding as %s for eDirectory authorization checks", autz_ctx->dn);
1624 
1625  /*
1626  * Bind as the user
1627  */
1629  autz_ctx->status = LDAP_AUTZ_POST_EDIR;
1630  return fr_ldap_bind_auth_async(request, thread, autz_ctx->dn, password->vp_strvalue);
1631  }
1632  goto skip_edir;
1633 
1634  case LDAP_AUTZ_POST_EDIR:
1635  {
1636  /*
1637  * The result of the eDirectory user bind will be in p_result.
1638  * Anything other than RLM_MODULE_OK is a failure.
1639  */
1640  if (*p_result != RLM_MODULE_OK) {
1641  rcode = *p_result;
1642  goto finish;
1643  }
1644 
1645  }
1646  FALL_THROUGH;
1647 
1648 #endif
1649  case LDAP_AUTZ_MAP:
1650 #ifdef WITH_EDIR
1651  skip_edir:
1652 #endif
1653  if (!map_list_empty(call_env->user_map) || inst->valuepair_attr) {
1654  RDEBUG2("Processing user attributes");
1655  RINDENT();
1656  if (fr_ldap_map_do(request, inst->valuepair_attr,
1657  &autz_ctx->expanded, autz_ctx->entry) > 0) rcode = RLM_MODULE_UPDATED;
1658  REXDENT();
1659  rlm_ldap_check_reply(request, inst, autz_ctx->dlinst->name, call_env->expect_password->vb_bool, autz_ctx->ttrunk);
1660  }
1661  FALL_THROUGH;
1662 
1664  /*
1665  * Apply ONE user profile, or a default user profile.
1666  */
1667  if (call_env->default_profile.type == FR_TYPE_STRING) {
1668  unlang_action_t ret;
1669 
1671  ret = rlm_ldap_map_profile(NULL, inst, request, autz_ctx->ttrunk, autz_ctx->profile_value,
1672  inst->profile_scope, call_env->default_profile.vb_strvalue, &autz_ctx->expanded);
1673  switch (ret) {
1674  case UNLANG_ACTION_FAIL:
1675  rcode = RLM_MODULE_FAIL;
1676  goto finish;
1677 
1681 
1682  default:
1683  break;
1684  }
1685  }
1686  FALL_THROUGH;
1687 
1689  /*
1690  * Did we jump back her after applying the default profile?
1691  */
1692  if (autz_ctx->status == LDAP_AUTZ_POST_DEFAULT_PROFILE) {
1693  rcode = RLM_MODULE_UPDATED;
1694  }
1695  /*
1696  * Apply a SET of user profiles.
1697  */
1698  switch (autz_ctx->access_state) {
1699  case LDAP_ACCESS_ALLOWED:
1700  if (inst->profile_attr) {
1701  int count;
1702 
1703  autz_ctx->profile_values = ldap_get_values_len(handle, autz_ctx->entry, inst->profile_attr);
1704  count = ldap_count_values_len(autz_ctx->profile_values);
1705  if (count > 0) {
1706  RDEBUG2("Processing %i profile(s) found in attribute \"%s\"", count, inst->profile_attr);
1707  if (RDEBUG_ENABLED3) {
1708  for (struct berval **bv_p = autz_ctx->profile_values; *bv_p; bv_p++) {
1709  RDEBUG3("Will evaluate profile with DN \"%pV\"", fr_box_strvalue_len((*bv_p)->bv_val, (*bv_p)->bv_len));
1710  }
1711  }
1712  } else {
1713  RDEBUG2("No profile(s) found in attribute \"%s\"", inst->profile_attr);
1714  }
1715  }
1716  break;
1717 
1718  case LDAP_ACCESS_SUSPENDED:
1719  if (inst->profile_attr_suspend) {
1720  int count;
1721 
1722  autz_ctx->profile_values = ldap_get_values_len(handle, autz_ctx->entry, inst->profile_attr_suspend);
1723  count = ldap_count_values_len(autz_ctx->profile_values);
1724  if (count > 0) {
1725  RDEBUG2("Processing %i suspension profile(s) found in attribute \"%s\"", count, inst->profile_attr_suspend);
1726  if (RDEBUG_ENABLED3) {
1727  for (struct berval **bv_p = autz_ctx->profile_values; *bv_p; bv_p++) {
1728  RDEBUG3("Will evaluate suspenension profile with DN \"%pV\"",
1729  fr_box_strvalue_len((*bv_p)->bv_val, (*bv_p)->bv_len));
1730  }
1731  }
1732  } else {
1733  RDEBUG2("No suspension profile(s) found in attribute \"%s\"", inst->profile_attr_suspend);
1734  }
1735  }
1736  break;
1737 
1739  break;
1740  }
1741 
1742  FALL_THROUGH;
1743 
1745  /*
1746  * After each profile has been applied, execution will restart here.
1747  * Start by clearing the previously used value.
1748  */
1749  if (autz_ctx->profile_value) {
1750  TALLOC_FREE(autz_ctx->profile_value);
1751  rcode = RLM_MODULE_UPDATED; /* We're back here after applying a profile successfully */
1752  }
1753 
1754  if (autz_ctx->profile_values && autz_ctx->profile_values[autz_ctx->value_idx]) {
1755  unlang_action_t ret;
1756 
1757  autz_ctx->profile_value = fr_ldap_berval_to_string(autz_ctx, autz_ctx->profile_values[autz_ctx->value_idx++]);
1759  ret = rlm_ldap_map_profile(NULL, inst, request, autz_ctx->ttrunk, autz_ctx->profile_value,
1760  inst->profile_scope, autz_ctx->call_env->profile_filter.vb_strvalue, &autz_ctx->expanded);
1761  switch (ret) {
1762  case UNLANG_ACTION_FAIL:
1763  rcode = RLM_MODULE_FAIL;
1764  goto finish;
1765 
1767  autz_ctx->status = LDAP_AUTZ_USER_PROFILE;
1769 
1770  default:
1771  break;
1772  }
1773  }
1774  break;
1775  }
1776 
1777 finish:
1778  talloc_free(autz_ctx);
1779 
1780  RETURN_MODULE_RCODE(rcode);
1781 }
1782 
1783 /** Clear up when cancelling a mod_authorize call
1784  *
1785  */
1786 static void mod_authorize_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
1787 {
1788  ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, ldap_autz_ctx_t);
1789 
1790  if (autz_ctx->query && autz_ctx->query->treq) fr_trunk_request_signal_cancel(autz_ctx->query->treq);
1791 }
1792 
1793 /** Ensure authorization context is properly cleared up
1794  *
1795  */
1796 static int autz_ctx_free(ldap_autz_ctx_t *autz_ctx)
1797 {
1798  talloc_free(autz_ctx->expanded.ctx);
1799  if (autz_ctx->profile_values) ldap_value_free_len(autz_ctx->profile_values);
1800  return 0;
1801 }
1802 
1803 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1804 {
1806  fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
1807  ldap_autz_ctx_t *autz_ctx;
1808  fr_ldap_map_exp_t *expanded;
1809  ldap_autz_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_autz_call_env_t);
1810 
1811  MEM(autz_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_autz_ctx_t));
1812  talloc_set_destructor(autz_ctx, autz_ctx_free);
1813  expanded = &autz_ctx->expanded;
1814 
1815  /*
1816  * Don't be tempted to add a check for User-Name or
1817  * User-Password here. LDAP authorization can be used
1818  * for many things besides searching for users.
1819  */
1820  if (fr_ldap_map_expand(autz_ctx, expanded, request, call_env->user_map, inst->valuepair_attr) < 0) {
1821  fail:
1822  talloc_free(autz_ctx);
1824  }
1825 
1826  autz_ctx->ttrunk = fr_thread_ldap_trunk_get(thread, inst->handle_config.server, inst->handle_config.admin_identity,
1827  inst->handle_config.admin_password, request, &inst->handle_config);
1828  if (!autz_ctx->ttrunk) goto fail;
1829 
1830 #define CHECK_EXPANDED_SPACE(_expanded) fr_assert((size_t)_expanded->count < (NUM_ELEMENTS(_expanded->attrs) - 1));
1831 
1832  /*
1833  * Add any additional attributes we need for checking access, memberships, and profiles
1834  */
1835  if (inst->user.obj_access_attr) {
1836  CHECK_EXPANDED_SPACE(expanded);
1837  expanded->attrs[expanded->count++] = inst->user.obj_access_attr;
1838  }
1839 
1840  if (inst->group.userobj_membership_attr && (inst->group.cacheable_dn || inst->group.cacheable_name)) {
1841  CHECK_EXPANDED_SPACE(expanded);
1842  expanded->attrs[expanded->count++] = inst->group.userobj_membership_attr;
1843  }
1844 
1845  if (inst->profile_attr) {
1846  CHECK_EXPANDED_SPACE(expanded);
1847  expanded->attrs[expanded->count++] = inst->profile_attr;
1848  }
1849 
1850  if (inst->profile_attr_suspend) {
1851  CHECK_EXPANDED_SPACE(expanded);
1852  expanded->attrs[expanded->count++] = inst->profile_attr_suspend;
1853  }
1854  expanded->attrs[expanded->count] = NULL;
1855 
1856  autz_ctx->dlinst = mctx->inst;
1857  autz_ctx->inst = inst;
1858  autz_ctx->call_env = call_env;
1859  autz_ctx->status = LDAP_AUTZ_FIND;
1860 
1862  ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, autz_ctx) < 0) RETURN_MODULE_FAIL;
1863 
1865 }
1866 
1867 /** Perform async lookup of user DN if required for user modification
1868  *
1869  */
1871  request_t *request, void *uctx)
1872 {
1873  ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(uctx, ldap_user_modify_ctx_t);
1874 
1875  return rlm_ldap_find_user_async(usermod_ctx, usermod_ctx->inst, request, &usermod_ctx->call_env->user_base,
1876  &usermod_ctx->call_env->user_filter, usermod_ctx->ttrunk, NULL, NULL);
1877 }
1878 
1879 /** Cancel an in progress user modification.
1880  *
1881  */
1882 static void user_modify_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
1883 {
1884  ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(uctx, ldap_user_modify_ctx_t);
1885 
1886  if (!usermod_ctx->query || !usermod_ctx->query->treq) return;
1887 
1888  fr_trunk_request_signal_cancel(usermod_ctx->query->treq);
1889 }
1890 
1891 /** Handle results of user modification.
1892  *
1893  */
1894 static unlang_action_t user_modify_final(rlm_rcode_t *p_result, UNUSED int *priority,
1895  request_t *request, void *uctx)
1896 {
1897  ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(uctx, ldap_user_modify_ctx_t);
1898  fr_ldap_query_t *query = usermod_ctx->query;
1899  rlm_rcode_t rcode = RLM_MODULE_OK;
1900 
1901  switch (query->ret) {
1902  case LDAP_RESULT_SUCCESS:
1903  break;
1904 
1905  case LDAP_RESULT_NO_RESULT:
1906  case LDAP_RESULT_BAD_DN:
1907  RDEBUG2("User object \"%s\" not modified", usermod_ctx->dn);
1908  rcode = RLM_MODULE_INVALID;
1909  break;
1910 
1911  default:
1912  rcode = RLM_MODULE_FAIL;
1913  break;
1914  }
1915 
1916  talloc_free(usermod_ctx);
1917  RETURN_MODULE_RCODE(rcode);
1918 }
1919 
1920 /** Take the retrieved user DN and launch the async modification.
1921  *
1922  */
1923 static unlang_action_t user_modify_resume(rlm_rcode_t *p_result, UNUSED int *priority,
1924  request_t *request, void *uctx)
1925 {
1926  ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(uctx, ldap_user_modify_ctx_t);
1927  LDAPMod **modify = usermod_ctx->mod_p;
1928 
1929  /*
1930  * If an LDAP search was used to find the user DN
1931  * usermod_ctx->dn will be NULL.
1932  */
1933  if (!usermod_ctx->dn) usermod_ctx->dn = rlm_find_user_dn_cached(request);
1934 
1935  if (!usermod_ctx->dn) {
1936  fail:
1937  talloc_free(usermod_ctx);
1939  }
1940 
1941  if (unlang_function_push(request, NULL, user_modify_final, user_modify_cancel, ~FR_SIGNAL_CANCEL,
1942  UNLANG_SUB_FRAME, usermod_ctx) < 0) goto fail;
1943 
1944  return fr_ldap_trunk_modify(usermod_ctx, &usermod_ctx->query, request, usermod_ctx->ttrunk,
1945  usermod_ctx->dn, modify, NULL, NULL);
1946 }
1947 
1948 /** Modify user's object in LDAP
1949  *
1950  * Process a modification map to update a user object in the LDAP directory.
1951  *
1952  * @param[out] p_result the result of the modification.
1953  * @param[in] inst rlm_ldap instance.
1954  * @param[in] request Current request.
1955  * @param[in] section that holds the map to process.
1956  * @param[in] call_env Call environment. Contains expanded base and filter to find user.
1957  * @return one of the RLM_MODULE_* values.
1958  */
1960  ldap_acct_section_t *section, ldap_usermod_call_env_t *call_env)
1961 {
1962  rlm_rcode_t rcode = RLM_MODULE_FAIL;
1963  fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
1964  ldap_user_modify_ctx_t *usermod_ctx = NULL;
1965 
1966  int total = 0, last_pass = 0;
1967 
1968  char const *attr;
1969  char const *value;
1970 
1971  /*
1972  * Build our set of modifications using the update sections in
1973  * the config.
1974  */
1975  CONF_ITEM *ci;
1976  CONF_PAIR *cp;
1977  CONF_SECTION *cs;
1978  fr_token_t op;
1979  char path[FR_MAX_STRING_LEN];
1980 
1981  char *p = path;
1982 
1983  fr_assert(section);
1984 
1985  /*
1986  * Locate the update section were going to be using
1987  */
1988  if (section->reference[0] != '.') *p++ = '.';
1989 
1990  if (xlat_eval(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) goto error;
1991 
1992  ci = cf_reference_item(NULL, section->cs, path);
1993  if (!ci) goto error;
1994 
1995  if (!cf_item_is_section(ci)){
1996  REDEBUG("Reference must resolve to a section");
1997 
1998  goto error;
1999  }
2000 
2001  cs = cf_section_find(cf_item_to_section(ci), "update", NULL);
2002  if (!cs) {
2003  REDEBUG("Section must contain 'update' subsection");
2004 
2005  goto error;
2006  }
2007 
2008  MEM(usermod_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_user_modify_ctx_t));
2009  *usermod_ctx = (ldap_user_modify_ctx_t) {
2010  .inst = inst,
2011  .call_env = call_env
2012  };
2013 
2014  /*
2015  * Iterate over all the pairs, building our mods array
2016  */
2017  for (ci = cf_item_next(cs, NULL); ci != NULL; ci = cf_item_next(cs, ci)) {
2018  bool do_xlat = false;
2019 
2020  if (total == LDAP_MAX_ATTRMAP) {
2021  REDEBUG("Modify map size exceeded");
2022 
2023  goto error;
2024  }
2025 
2026  if (!cf_item_is_pair(ci)) {
2027  REDEBUG("Entry is not in \"ldap-attribute = value\" format");
2028 
2029  goto error;
2030  }
2031 
2032  /*
2033  * Retrieve all the information we need about the pair
2034  */
2035  cp = cf_item_to_pair(ci);
2036  value = cf_pair_value(cp);
2037  attr = cf_pair_attr(cp);
2038  op = cf_pair_operator(cp);
2039 
2040  if (!value || (*value == '\0')) {
2041  RDEBUG2("Empty value string, skipping attribute \"%s\"", attr);
2042 
2043  continue;
2044  }
2045 
2046  switch (cf_pair_value_quote(cp)) {
2047  case T_BARE_WORD:
2049  break;
2050 
2051  case T_BACK_QUOTED_STRING:
2053  do_xlat = true;
2054  break;
2055 
2056  default:
2057  fr_assert(0);
2058  goto error;
2059  }
2060 
2061  if (op == T_OP_CMP_FALSE) {
2062  usermod_ctx->passed[last_pass] = NULL;
2063  } else if (do_xlat) {
2064  char *exp = NULL;
2065 
2066  if (xlat_aeval(usermod_ctx, &exp, request, value, NULL, NULL) <= 0) {
2067  RDEBUG2("Skipping attribute \"%s\"", attr);
2068  talloc_free(exp);
2069 
2070  continue;
2071  }
2072 
2073  usermod_ctx->passed[last_pass] = exp;
2074  /*
2075  * Static strings
2076  */
2077  } else {
2078  memcpy(&(usermod_ctx->passed[last_pass]), &value, sizeof(usermod_ctx->passed[last_pass]));
2079  }
2080 
2081  usermod_ctx->passed[last_pass + 1] = NULL;
2082  usermod_ctx->mod_s[total].mod_values = &(usermod_ctx->passed[last_pass]);
2083  last_pass += 2;
2084 
2085  switch (op) {
2086  /*
2087  * T_OP_EQ is *NOT* supported, it is impossible to
2088  * support because of the lack of transactions in LDAP
2089  */
2090  case T_OP_ADD_EQ:
2091  usermod_ctx->mod_s[total].mod_op = LDAP_MOD_ADD;
2092  break;
2093 
2094  case T_OP_SET:
2095  usermod_ctx->mod_s[total].mod_op = LDAP_MOD_REPLACE;
2096  break;
2097 
2098  case T_OP_SUB_EQ:
2099  case T_OP_CMP_FALSE:
2100  usermod_ctx->mod_s[total].mod_op = LDAP_MOD_DELETE;
2101  break;
2102 
2103  case T_OP_INCRM:
2104  usermod_ctx->mod_s[total].mod_op = LDAP_MOD_INCREMENT;
2105  break;
2106 
2107  default:
2108  REDEBUG("Operator '%s' is not supported for LDAP modify operations",
2109  fr_table_str_by_value(fr_tokens_table, op, "<INVALID>"));
2110 
2111  goto error;
2112  }
2113 
2114  /*
2115  * Now we know the value is ok, copy the pointers into
2116  * the ldapmod struct.
2117  */
2118  memcpy(&(usermod_ctx->mod_s[total].mod_type), &attr, sizeof(usermod_ctx->mod_s[total].mod_type));
2119 
2120  usermod_ctx->mod_p[total] = &(usermod_ctx->mod_s[total]);
2121  total++;
2122  }
2123 
2124  if (total == 0) {
2125  rcode = RLM_MODULE_NOOP;
2126  goto release;
2127  }
2128 
2129  usermod_ctx->mod_p[total] = NULL;
2130 
2131  usermod_ctx->ttrunk = fr_thread_ldap_trunk_get(thread, inst->handle_config.server,
2132  inst->handle_config.admin_identity,
2133  inst->handle_config.admin_password,
2134  request, &inst->handle_config);
2135  if (!usermod_ctx->ttrunk) {
2136  REDEBUG("Unable to get LDAP trunk for update");
2137  talloc_free(usermod_ctx);
2139  }
2140 
2141  usermod_ctx->dn = rlm_find_user_dn_cached(request);
2142 
2143  if (unlang_function_push(request, usermod_ctx->dn ? NULL : user_modify_start, user_modify_resume,
2144  NULL, 0, UNLANG_SUB_FRAME, usermod_ctx) < 0) goto error;
2145 
2147 
2148 release:
2149 error:
2150  TALLOC_FREE(usermod_ctx);
2151  RETURN_MODULE_RCODE(rcode);
2152 }
2153 
2154 static unlang_action_t CC_HINT(nonnull) mod_accounting(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
2155 {
2157  ldap_usermod_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_call_env_t);
2158 
2159  if (inst->accounting) return user_modify(p_result, inst, request, inst->accounting, call_env);
2160 
2162 }
2163 
2164 static unlang_action_t CC_HINT(nonnull) mod_post_auth(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
2165 {
2167  ldap_usermod_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_call_env_t);
2168 
2169  if (inst->postauth) return user_modify(p_result, inst, request, inst->postauth, call_env);
2170 
2172 }
2173 
2174 
2175 /** Detach from the LDAP server and cleanup internal state.
2176  *
2177  */
2178 static int mod_detach(module_detach_ctx_t const *mctx)
2179 {
2180  rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t);
2181 
2182  if (inst->user.obj_sort_ctrl) ldap_control_free(inst->user.obj_sort_ctrl);
2183 
2184  return 0;
2185 }
2186 
2187 /** Parse an accounting sub section.
2188  *
2189  * Allocate a new ldap_acct_section_t and write the config data into it.
2190  *
2191  * @param[in] mctx rlm_ldap configuration.
2192  * @param[in] parent of the config section.
2193  * @param[out] config to write the sub section parameters to.
2194  * @param[in] comp The section name were parsing the config for.
2195  * @return
2196  * - 0 on success.
2197  * - < 0 on failure.
2198  */
2199 static int parse_sub_section(module_inst_ctx_t const *mctx,
2202 {
2203  rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t);
2204  CONF_SECTION *cs;
2205 
2206  char const *name = section_type_value[comp];
2207 
2208  cs = cf_section_find(parent, name, NULL);
2209  if (!cs) {
2210  DEBUG2("rlm_ldap (%s) - Couldn't find configuration for %s, will return NOOP for calls "
2211  "from this section", mctx->inst->name, name);
2212 
2213  return 0;
2214  }
2215 
2216  if (cf_section_rules_push(cs, acct_section_config) < 0) return -1;
2217 
2218  *config = talloc_zero(inst, ldap_acct_section_t);
2219  if (cf_section_parse(*config, *config, cs) < 0) {
2220  PERROR("rlm_ldap (%s) - Failed parsing configuration for section %s", mctx->inst->name, name);
2221 
2222  return -1;
2223  }
2224 
2225  (*config)->cs = cs;
2226 
2227  return 0;
2228 }
2229 
2230 /** Initialise thread specific data structure
2231  *
2232  */
2234 {
2235  rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t);
2236  fr_ldap_thread_t *t = talloc_get_type_abort(mctx->thread, fr_ldap_thread_t);
2237  fr_ldap_thread_trunk_t *ttrunk;
2238 
2239  /*
2240  * Initialise tree for connection trunks used by this thread
2241  */
2243 
2244  t->config = &inst->handle_config;
2245  t->trunk_conf = &inst->trunk_conf;
2246  t->bind_trunk_conf = &inst->bind_trunk_conf;
2247  t->el = mctx->el;
2248 
2249  /*
2250  * Launch trunk for module default connection
2251  */
2252  ttrunk = fr_thread_ldap_trunk_get(t, inst->handle_config.server, inst->handle_config.admin_identity,
2253  inst->handle_config.admin_password, NULL, &inst->handle_config);
2254  if (!ttrunk) {
2255  ERROR("Unable to launch LDAP trunk");
2256  return -1;
2257  }
2258 
2259  /*
2260  * Set up a per-thread LDAP trunk to use for bind auths
2261  */
2263 
2265 
2266  return 0;
2267 }
2268 
2269 /** Clean up thread specific data structure
2270  *
2271  */
2273 {
2274  fr_ldap_thread_t *t = talloc_get_type_abort(mctx->thread, fr_ldap_thread_t);
2275  void **trunks_to_free;
2276  int i;
2277 
2278  if (fr_rb_flatten_inorder(NULL, &trunks_to_free, t->trunks) < 0) return -1;
2279 
2280  for (i = talloc_array_length(trunks_to_free) - 1; i >= 0; i--) talloc_free(trunks_to_free[i]);
2281  talloc_free(trunks_to_free);
2282  talloc_free(t->trunks);
2283 
2284  return 0;
2285 }
2286 
2287 /** Bootstrap the module
2288  *
2289  * Define attributes.
2290  *
2291  * @param[in] mctx configuration data.
2292  * @return
2293  * - 0 on success.
2294  * - < 0 on failure.
2295  */
2296 static int mod_bootstrap(module_inst_ctx_t const *mctx)
2297 {
2298  rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t);
2299  CONF_SECTION *conf = mctx->inst->conf;
2300  char buffer[256];
2301  char const *group_attribute;
2302  xlat_t *xlat;
2303 
2304  inst->handle_config.name = talloc_typed_asprintf(inst, "rlm_ldap (%s)", mctx->inst->name);
2305 
2306  if (inst->group.attribute) {
2307  group_attribute = inst->group.attribute;
2308  } else if (cf_section_name2(conf)) {
2309  snprintf(buffer, sizeof(buffer), "%s-LDAP-Group", mctx->inst->name);
2310  group_attribute = buffer;
2311  } else {
2312  group_attribute = "LDAP-Group";
2313  }
2314 
2315  inst->group.da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), group_attribute);
2316 
2317  /*
2318  * If the group attribute was not in the dictionary, create it
2319  */
2320  if (!inst->group.da) {
2321  fr_dict_attr_flags_t flags;
2322 
2323  memset(&flags, 0, sizeof(flags));
2325  group_attribute, -1, FR_TYPE_STRING, &flags) < 0) {
2326  PERROR("Error creating group attribute");
2327  return -1;
2328 
2329  }
2330  inst->group.da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), group_attribute);
2331  }
2332 
2333  /*
2334  * Setup the cache attribute
2335  */
2336  if (inst->group.cache_attribute) {
2337  fr_dict_attr_flags_t flags;
2338 
2339  memset(&flags, 0, sizeof(flags));
2341  inst->group.cache_attribute, -1, FR_TYPE_STRING, &flags) < 0) {
2342  PERROR("Error creating cache attribute");
2343  return -1;
2344 
2345  }
2346  inst->group.cache_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), inst->group.cache_attribute);
2347  } else {
2348  inst->group.cache_da = inst->group.da; /* Default to the group_da */
2349  }
2350 
2351  /*
2352  * Trunks used for bind auth can only have one request in flight per connection.
2353  */
2354  inst->bind_trunk_conf.target_req_per_conn = 1;
2355  inst->bind_trunk_conf.max_req_per_conn = 1;
2356 
2357  /*
2358  * Set sizes for trunk request pool.
2359  */
2360  inst->bind_trunk_conf.req_pool_headers = 2;
2361  inst->bind_trunk_conf.req_pool_size = sizeof(fr_ldap_bind_auth_ctx_t) + sizeof(fr_ldap_sasl_ctx_t);
2362 
2363  xlat = xlat_func_register_module(NULL, mctx, NULL, ldap_xlat, FR_TYPE_STRING);
2365 
2366  if (unlikely(!(xlat = xlat_func_register_module(NULL, mctx, "memberof", ldap_memberof_xlat,
2367  FR_TYPE_BOOL)))) return -1;
2370 
2371  if (unlikely(!(xlat = xlat_func_register_module(NULL, mctx, "profile", ldap_profile_xlat,
2372  FR_TYPE_BOOL)))) return -1;
2375 
2377 
2378  return 0;
2379 }
2380 
2381 static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
2382  CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2,
2383  UNUSED void const *data, call_env_parser_t const *rule)
2384 {
2385  map_list_t *maps;
2386  CONF_SECTION *update = cf_item_to_section(ci);
2387  ldap_update_rules_t const *ur = rule->uctx;
2388 
2389  bool expect_password = false;
2390 
2391  /*
2392  * Build the attribute map
2393  */
2394  {
2395  map_t const *map = NULL;
2396  tmpl_attr_t const *ar;
2397  call_env_parsed_t *parsed;
2398 
2399  MEM(parsed = call_env_parsed_add(ctx, out,
2400  &(call_env_parser_t){
2401  .name = "update",
2402  .flags = CALL_ENV_FLAG_PARSE_ONLY,
2403  .pair = {
2404  .parsed = {
2405  .offset = ur->map_offset,
2406  .type = CALL_ENV_PARSE_TYPE_VOID
2407  }
2408  }
2409  }));
2410 
2411  MEM(maps = talloc(parsed, map_list_t));
2412  map_list_init(maps);
2413 
2414  if (update && (map_afrom_cs(maps, maps, update, t_rules, t_rules, fr_ldap_map_verify,
2415  NULL, LDAP_MAX_ATTRMAP)) < 0) {
2416  call_env_parsed_free(out, parsed);
2417  return -1;
2418  }
2419  /*
2420  * Check map to see if a password is being retrieved.
2421  * fr_ldap_map_verify ensures that all maps have attributes on the LHS.
2422  * All passwords have a common parent attribute of attr_password
2423  */
2424  while ((map = map_list_next(maps, map))) {
2425  ar = tmpl_attr_tail(map->lhs);
2426  if (ar->da->parent == attr_password) {
2427  expect_password = true;
2428  break;
2429  }
2430  }
2431  call_env_parsed_set_data(parsed, maps);
2432  }
2433 
2434  /*
2435  * Write out whether we expect a password to be returned from the ldap data
2436  */
2437  if (ur->expect_password_offset >= 0) {
2438  call_env_parsed_t *parsed;
2439  fr_value_box_t *vb;
2440 
2441  MEM(parsed = call_env_parsed_add(ctx, out,
2442  &(call_env_parser_t){
2443  .name = "expect_password",
2444  .flags = CALL_ENV_FLAG_PARSE_ONLY,
2445  .pair = {
2446  .parsed = {
2447  .offset = ur->expect_password_offset,
2449  }
2450  }
2451  }));
2452  MEM(vb = fr_value_box_alloc(parsed, FR_TYPE_BOOL, NULL));
2453  vb->vb_bool = expect_password;
2454  call_env_parsed_set_value(parsed, vb);
2455  }
2456 
2457  return 0;
2458 }
2459 
2460 static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci,
2461  UNUSED char const *section_name1, UNUSED char const *section_name2,
2462  void const *data, UNUSED call_env_parser_t const *rule)
2463 {
2465  char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
2466  tmpl_t *parsed;
2467 
2468  if (fr_ldap_filter_to_tmpl(ctx, t_rules, filters, NUM_ELEMENTS(filters), &parsed) < 0) return -1;
2469 
2470  *(void **)out = parsed;
2471  return 0;
2472 }
2473 
2474 /** Instantiate the module
2475  *
2476  * Creates a new instance of the module reading parameters from a configuration section.
2477  *
2478  * @param [in] mctx configuration data.
2479  * @return
2480  * - 0 on success.
2481  * - < 0 on failure.
2482  */
2483 static int mod_instantiate(module_inst_ctx_t const *mctx)
2484 {
2485  size_t i;
2486 
2487  CONF_SECTION *options;
2488  rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t);
2489  CONF_SECTION *conf = mctx->inst->conf;
2490 
2491  options = cf_section_find(conf, "options", NULL);
2492  if (!options || !cf_pair_find(options, "chase_referrals")) {
2493  inst->handle_config.chase_referrals_unset = true; /* use OpenLDAP defaults */
2494  }
2495 
2496  /*
2497  * If the configuration parameters can't be parsed, then fail.
2498  */
2499  if ((parse_sub_section(mctx, conf, &inst->accounting, MOD_ACCOUNTING) < 0) ||
2500  (parse_sub_section(mctx, conf, &inst->postauth, MOD_POST_AUTH) < 0)) {
2501  cf_log_err(conf, "Failed parsing configuration");
2502 
2503  goto error;
2504  }
2505 
2506  /*
2507  * Sanity checks for cacheable groups code.
2508  */
2509  if (inst->group.cacheable_name && inst->group.obj_membership_filter) {
2510  if (!inst->group.obj_name_attr) {
2511  cf_log_err(conf, "Configuration item 'group.name_attribute' must be set if cacheable "
2512  "group names are enabled");
2513 
2514  goto error;
2515  }
2516  }
2517 
2518  /*
2519  * If we have a *pair* as opposed to a *section*
2520  * then the module is referencing another ldap module's
2521  * connection pool.
2522  */
2523  if (!cf_pair_find(conf, "pool")) {
2524  if (!inst->handle_config.server_str) {
2525  cf_log_err(conf, "Configuration item 'server' must have a value");
2526  goto error;
2527  }
2528  }
2529 
2530 #ifndef WITH_SASL
2531  if (inst->handle_config.admin_sasl.mech) {
2532  cf_log_err(conf, "Configuration item 'sasl.mech' not supported. "
2533  "Linked libldap does not provide ldap_sasl_interactive_bind function");
2534  goto error;
2535  }
2536 #endif
2537 
2538  /*
2539  * Initialise server with zero length string to
2540  * make code below simpler.
2541  */
2542  inst->handle_config.server = talloc_strdup(inst, "");
2543 
2544  /*
2545  * Now iterate over all the 'server' config items
2546  */
2547  for (i = 0; i < talloc_array_length(inst->handle_config.server_str); i++) {
2548  char const *value = inst->handle_config.server_str[i];
2549  size_t j;
2550 
2551  /*
2552  * Explicitly prevent multiple server definitions
2553  * being used in the same string.
2554  */
2555  for (j = 0; j < talloc_array_length(value) - 1; j++) {
2556  switch (value[j]) {
2557  case ' ':
2558  case ',':
2559  case ';':
2560  cf_log_err(conf, "Invalid character '%c' found in 'server' configuration item",
2561  value[j]);
2562  goto error;
2563 
2564  default:
2565  continue;
2566  }
2567  }
2568 
2569  /*
2570  * Split original server value out into URI, server and port
2571  * so whatever initialization function we use later will have
2572  * the server information in the format it needs.
2573  */
2574  if (ldap_is_ldap_url(value)) {
2575  if (fr_ldap_server_url_check(&inst->handle_config, value, conf) < 0) return -1;
2576  } else
2577  /*
2578  * If it's not an URL, then just treat server as a hostname.
2579  */
2580  {
2581  if (fr_ldap_server_config_check(&inst->handle_config, value, conf) < 0) return -1;
2582  }
2583  }
2584 
2585  /*
2586  * inst->handle_config.server be unset if connection pool sharing is used.
2587  */
2588  if (inst->handle_config.server) {
2589  inst->handle_config.server[talloc_array_length(inst->handle_config.server) - 2] = '\0';
2590  DEBUG4("rlm_ldap (%s) - LDAP server string: %s", mctx->inst->name, inst->handle_config.server);
2591  }
2592 
2593  /*
2594  * Workaround for servers which support LDAPS but not START TLS
2595  */
2596  if (inst->handle_config.port == LDAPS_PORT || inst->handle_config.tls_mode) {
2597  inst->handle_config.tls_mode = LDAP_OPT_X_TLS_HARD;
2598  } else {
2599  inst->handle_config.tls_mode = 0;
2600  }
2601 
2602  /*
2603  * Convert dereference strings to enumerated constants
2604  */
2605  if (inst->handle_config.dereference_str) {
2606  inst->handle_config.dereference = fr_table_value_by_str(fr_ldap_dereference,
2607  inst->handle_config.dereference_str, -1);
2608  if (inst->handle_config.dereference < 0) {
2609  cf_log_err(conf, "Invalid 'dereference' value \"%s\", expected 'never', 'searching', "
2610  "'finding' or 'always'", inst->handle_config.dereference_str);
2611  goto error;
2612  }
2613  }
2614 
2615  /*
2616  * Build the server side sort control for user objects
2617  */
2618  if (inst->user.obj_sort_by) {
2619  LDAPSortKey **keys;
2620  int ret;
2621 
2622  ret = ldap_create_sort_keylist(&keys, UNCONST(char *, inst->user.obj_sort_by));
2623  if (ret != LDAP_SUCCESS) {
2624  cf_log_err(conf, "Invalid user.sort_by value \"%s\": %s",
2625  inst->user.obj_sort_by, ldap_err2string(ret));
2626  goto error;
2627  }
2628 
2629  /*
2630  * Always set the control as critical, if it's not needed
2631  * the user can comment it out...
2632  */
2633  ret = ldap_create_sort_control(ldap_global_handle, keys, 1, &inst->user.obj_sort_ctrl);
2634  ldap_free_sort_keylist(keys);
2635  if (ret != LDAP_SUCCESS) {
2636  ERROR("Failed creating server sort control: %s", ldap_err2string(ret));
2637  goto error;
2638  }
2639  }
2640 
2641  if (inst->handle_config.tls_require_cert_str) {
2642  /*
2643  * Convert cert strictness to enumerated constants
2644  */
2645  inst->handle_config.tls_require_cert = fr_table_value_by_str(fr_ldap_tls_require_cert,
2646  inst->handle_config.tls_require_cert_str, -1);
2647  if (inst->handle_config.tls_require_cert < 0) {
2648  cf_log_err(conf, "Invalid 'tls.require_cert' value \"%s\", expected 'never', "
2649  "'demand', 'allow', 'try' or 'hard'", inst->handle_config.tls_require_cert_str);
2650  goto error;
2651  }
2652  }
2653 
2654  if (inst->handle_config.tls_min_version_str) {
2655  if (strcmp(inst->handle_config.tls_min_version_str, "1.2") == 0) {
2656  inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_2;
2657 
2658  } else if (strcmp(inst->handle_config.tls_min_version_str, "1.1") == 0) {
2659  inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_1;
2660 
2661  } else if (strcmp(inst->handle_config.tls_min_version_str, "1.0") == 0) {
2662  inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_0;
2663 
2664  } else {
2665  cf_log_err(conf, "Invalid 'tls.tls_min_version' value \"%s\"", inst->handle_config.tls_min_version_str);
2666  goto error;
2667  }
2668  }
2669 
2670  return 0;
2671 
2672 error:
2673  return -1;
2674 }
2675 
2676 static int mod_load(void)
2677 {
2678  xlat_t *xlat;
2679 
2680  if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.escape", ldap_uri_escape_xlat, FR_TYPE_STRING)))) return -1;
2683  xlat_func_safe_for_set(xlat, LDAP_URI_SAFE_FOR); /* Used for all LDAP escaping */
2684 
2685  if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.safe", xlat_transparent, FR_TYPE_STRING)))) return -1;
2689 
2690  if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.unescape", ldap_uri_unescape_xlat, FR_TYPE_STRING)))) return -1;
2693 
2694  return 0;
2695 }
2696 
2697 static void mod_unload(void)
2698 {
2699  xlat_func_unregister("ldap.uri.escape");
2700  xlat_func_unregister("ldap.uri.safe");
2701  xlat_func_unregister("ldap.uri.unescape");
2702 }
2703 
2704 /* globally exported name */
2705 extern module_rlm_t rlm_ldap;
2707  .common = {
2708  .magic = MODULE_MAGIC_INIT,
2709  .name = "ldap",
2710  .flags = 0,
2711  .inst_size = sizeof(rlm_ldap_t),
2712  .config = module_config,
2713  .onload = mod_load,
2714  .unload = mod_unload,
2715  .bootstrap = mod_bootstrap,
2717  .detach = mod_detach,
2718  .thread_inst_size = sizeof(fr_ldap_thread_t),
2719  .thread_inst_type = "fr_ldap_thread_t",
2720  .thread_instantiate = mod_thread_instantiate,
2721  .thread_detach = mod_thread_detach,
2722  },
2723  .method_names = (module_method_name_t[]){
2724  /*
2725  * Hack to support old configurations
2726  */
2727  { .name1 = "authorize", .name2 = CF_IDENT_ANY, .method = mod_authorize,
2728  .method_env = &authorize_method_env },
2729 
2730  { .name1 = "recv", .name2 = CF_IDENT_ANY, .method = mod_authorize,
2731  .method_env = &authorize_method_env },
2732  { .name1 = "accounting", .name2 = CF_IDENT_ANY, .method = mod_accounting,
2733  .method_env = &usermod_method_env },
2734  { .name1 = "authenticate", .name2 = CF_IDENT_ANY, .method = mod_authenticate,
2735  .method_env = &authenticate_method_env },
2736  { .name1 = "send", .name2 = CF_IDENT_ANY, .method = mod_post_auth,
2737  .method_env = &usermod_method_env },
2739  }
2740 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition: action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition: action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
static int const char char buffer[256]
Definition: acutest.h:574
log_entry msg
Definition: acutest.h:794
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr)
Remove a call_env_parsed_t from the list of parsed call envs.
Definition: call_env.c:692
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition: call_env.c:605
void call_env_parsed_set_data(call_env_parsed_t *parsed, void const *data)
Assign data to a call_env_parsed_t.
Definition: call_env.c:662
void call_env_parsed_set_value(call_env_parsed_t *parsed, fr_value_box_t const *vb)
Assign a value box to a call_env_parsed_t.
Definition: call_env.c:648
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
Definition: call_env.h:341
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition: call_env.h:216
void const * uctx
User context for callback functions.
Definition: call_env.h:209
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition: call_env.h:388
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition: call_env.h:74
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl must contain an attribute reference.
Definition: call_env.h:84
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition: call_env.h:83
@ CALL_ENV_FLAG_NONE
Definition: call_env.h:72
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:73
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition: call_env.h:86
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition: call_env.h:78
@ CALL_ENV_PARSE_TYPE_VALUE_BOX
Output of the parsing phase is a single value box (static data).
Definition: call_env.h:59
@ CALL_ENV_PARSE_TYPE_VOID
Output of the parsing phase is undefined (a custom structure).
Definition: call_env.h:60
#define FR_CALL_ENV_SUBSECTION(_name, _ident2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition: call_env.h:378
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition: call_env.h:316
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition: call_env.h:365
Per method call config.
Definition: call_env.h:171
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
Definition: cf_file.c:3206
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition: cf_parse.c:985
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic function for parsing conf pair values as int.
Definition: cf_parse.c:1474
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define cf_section_rules_push(_cs, _rule)
Definition: cf_parse.h:658
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition: cf_parse.h:310
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition: cf_parse.h:282
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition: cf_parse.h:297
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition: cf_parse.h:420
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition: cf_parse.h:417
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:400
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
Common header for all CONF_* types.
Definition: cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition: cf_util.c:597
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:629
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition: cf_util.c:1495
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition: cf_util.c:970
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition: cf_util.c:1356
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1126
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
fr_token_t cf_pair_operator(CONF_PAIR const *pair)
Return the operator of a pair.
Definition: cf_util.c:1525
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:649
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition: cf_util.c:1555
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition: cf_util.c:583
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define cf_item_next(_ci, _prev)
Definition: cf_util.h:92
#define CF_IDENT_ANY
Definition: cf_util.h:78
@ MOD_POST_AUTH
7 methods index for postauth section.
Definition: components.h:37
@ MOD_ACCOUNTING
3 methods index for accounting section.
Definition: components.h:36
enum rlm_components rlm_components_t
The different section components of the server.
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:405
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition: dict_util.c:2860
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition: dict_util.c:4179
static fr_slen_t in
Definition: dict.h:645
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition: dict_util.c:1245
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Values of the encryption flags.
Definition: merged_model.c:139
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Test enumeration values.
Definition: dict_test.h:92
char const *_CONST name
Instance name.
Definition: dl_module.h:163
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
dl_module_inst_t const * inst
Definition: dl_module.h:87
#define fr_event_timer_in(...)
Definition: event.h:255
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
#define GLOBAL_LIB_TERMINATOR
Definition: global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition: global_lib.h:38
static xlat_action_t ldap_uri_escape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Escape LDAP string.
Definition: rlm_ldap.c:404
static xlat_action_t ldap_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Expand an LDAP URL into a query, and return a string result from that query.
Definition: rlm_ldap.c:663
static xlat_action_t ldap_profile_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Expand an LDAP URL into a query, applying the results using the user update map.
Definition: rlm_ldap.c:1014
static xlat_action_t ldap_memberof_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Check for a user being in a LDAP group.
Definition: rlm_ldap.c:888
static xlat_action_t ldap_uri_unescape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Unescape LDAP string.
Definition: rlm_ldap.c:459
unlang_action_t rlm_ldap_cacheable_userobj(rlm_rcode_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx, char const *attr)
Convert group membership information into attributes.
Definition: groups.c:443
unlang_action_t rlm_ldap_check_cached(rlm_rcode_t *p_result, rlm_ldap_t const *inst, request_t *request, fr_value_box_t const *check)
Check group membership attributes to see if a user is a member.
Definition: groups.c:1155
unlang_action_t rlm_ldap_cacheable_groupobj(rlm_rcode_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx)
Convert group membership information into attributes.
Definition: groups.c:700
unlang_action_t rlm_ldap_check_groupobj_dynamic(rlm_rcode_t *p_result, request_t *request, ldap_memberof_xlat_ctx_t *xlat_ctx)
Initiate an LDAP search to determine group membership, querying group objects.
Definition: groups.c:786
unlang_action_t rlm_ldap_check_userobj_dynamic(rlm_rcode_t *p_result, request_t *request, ldap_memberof_xlat_ctx_t *xlat_ctx)
Query the LDAP directory to check if a user object is a member of a group.
Definition: groups.c:1117
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition: interpret.c:1745
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1340
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1384
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
int fr_ldap_map_verify(map_t *map, void *instance)
size_t fr_ldap_uri_unescape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Converts escaped DNs and filter strings into normal.
Definition: util.c:164
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition: util.c:439
int fr_ldap_map_getvalue(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Callback for map_to_request.
Definition: map.c:39
size_t fr_ldap_uri_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Converts "bad" strings into ones which are safe for LDAP.
Definition: util.c:70
int fr_ldap_filter_to_tmpl(TALLOC_CTX *ctx, tmpl_rules_t const *t_rules, char const **sub, size_t sublen, tmpl_t **out))
Combine filters and tokenize to a tmpl.
Definition: util.c:517
struct berval ** values
libldap struct containing bv_val (char *) and length bv_len.
Definition: base.h:359
fr_ldap_control_t serverctrls[LDAP_MAX_CONTROLS]
Server controls specific to this query.
Definition: base.h:444
fr_trunk_request_t * treq
Trunk request this query is associated with.
Definition: base.h:451
fr_time_delta_t res_timeout
How long we wait for results.
Definition: base.h:296
char const * admin_password
Password used in administrative bind.
Definition: base.h:229
fr_ldap_config_t * config
Module instance config.
Definition: base.h:381
int count
Index on next free element.
Definition: base.h:373
fr_event_timer_t const * ev
Event for timing out the query.
Definition: base.h:454
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition: util.c:211
char * server
Initial server to bind to.
Definition: base.h:222
static int8_t fr_ldap_bind_auth_cmp(void const *one, void const *two)
Compare two ldap bind auth structures on msgid.
Definition: base.h:719
LDAP * handle
libldap handle.
Definition: base.h:331
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:993
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition: base.h:227
fr_ldap_result_code_t ret
Result code.
Definition: base.h:465
bool freeit
Whether the control should be freed after we've finished using it.
Definition: base.h:136
fr_rb_tree_t * trunks
Tree of LDAP trunks used by this thread.
Definition: base.h:380
fr_trunk_conf_t * trunk_conf
Module trunk config.
Definition: base.h:382
char const * fr_ldap_url_err_to_str(int ldap_url_err)
Translate the error code emitted from ldap_url_parse and friends into something accessible with fr_st...
Definition: util.c:740
int fr_ldap_server_url_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION const *cs)
Check an LDAP server entry in URL format is valid.
Definition: util.c:605
int count
Number of values.
Definition: base.h:361
#define LDAP_MAX_ATTRMAP
Maximum number of mappings between LDAP and.
Definition: base.h:96
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Definition: util.c:110
static int8_t fr_ldap_trunk_cmp(void const *one, void const *two)
Compare two ldap trunk structures on connection URI / DN.
Definition: base.h:690
int fr_ldap_server_config_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION *cs)
Check an LDAP server config in server:port format is valid.
Definition: util.c:699
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition: util.c:390
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition: base.h:452
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:1367
fr_trunk_conf_t * bind_trunk_conf
Trunk config for bind auth trunk.
Definition: base.h:383
fr_ldap_result_code_t
LDAP query result codes.
Definition: base.h:186
@ LDAP_RESULT_TIMEOUT
The query timed out.
Definition: base.h:190
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition: base.h:188
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition: base.h:192
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition: base.h:191
int fr_ldap_map_expand(TALLOC_CTX *ctx, fr_ldap_map_exp_t *expanded, request_t *request, map_list_t const *maps, char const *generic_attr)
Expand values in an attribute map where needed.
Definition: map.c:271
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition: base.h:94
#define LDAP_VIRTUAL_DN_ATTR
'Virtual' attribute which maps to the DN of the object.
Definition: base.h:113
int fr_ldap_parse_url_extensions(LDAPControl **sss, size_t sss_len, char *extensions[])
Parse a subset (just server side sort for now) of LDAP URL extensions.
Definition: util.c:309
LDAPMessage * result
Head of LDAP results list.
Definition: base.h:463
fr_event_list_t * el
Thread event list for callbacks / timeouts.
Definition: base.h:384
LDAPControl * control
LDAP control.
Definition: base.h:135
int fr_ldap_map_do(request_t *request, char const *valuepair_attr, fr_ldap_map_exp_t const *expanded, LDAPMessage *entry)
Convert attribute map into valuepairs.
Definition: map.c:323
unlang_action_t fr_ldap_edir_get_password(request_t *request, char const *dn, fr_ldap_thread_trunk_t *ttrunk, fr_dict_attr_t const *password_da)
Initiate retrieval of the universal password from Novell eDirectory.
Definition: edir.c:295
char const * attrs[LDAP_MAX_ATTRMAP+LDAP_MAP_RESERVED+1]
Reserve some space for access attributes.
Definition: base.h:370
fr_trunk_t * trunk
Connection trunk.
Definition: base.h:403
fr_ldap_thread_trunk_t * bind_trunk
LDAP trunk used for bind auths.
Definition: base.h:385
unlang_action_t fr_ldap_bind_auth_async(request_t *request, fr_ldap_thread_t *thread, char const *bind_dn, char const *password)
Initiate an async LDAP bind for authentication.
Definition: bind.c:319
TALLOC_CTX * ctx
Context to allocate new attributes in.
Definition: base.h:372
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition: base.h:386
LDAPURLDesc * ldap_url
parsed URL for current query if the source of the query was a URL.
Definition: base.h:424
Holds arguments for async bind auth requests.
Definition: base.h:608
Connection configuration.
Definition: base.h:219
Tracks the state of a libldap connection handle.
Definition: base.h:330
Result of expanding the RHS of a set of maps.
Definition: base.h:368
LDAP query structure.
Definition: base.h:420
Contains a collection of values.
Definition: base.h:358
Holds arguments for the async SASL bind operation.
Definition: base.h:501
Thread specific structure to manage LDAP trunk connections.
Definition: base.h:379
Thread LDAP trunk structure.
Definition: base.h:397
#define FR_LDAP_COMMON_CONF(_conf)
Definition: conf.h:19
LDAP * ldap_global_handle
Hack for OpenLDAP libldap global initialisation.
Definition: base.c:39
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition: base.c:1051
size_t fr_ldap_scope_len
Definition: base.c:75
global_lib_autoinst_t fr_libldap_global_config
Definition: base.c:134
unlang_action_t fr_ldap_trunk_modify(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *dn, LDAPMod *mods[], LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async modification LDAP query on a trunk connection.
Definition: base.c:740
fr_table_num_sorted_t const fr_ldap_tls_require_cert[]
Definition: base.c:77
fr_table_num_sorted_t const fr_ldap_dereference[]
Definition: base.c:86
fr_ldap_query_t * fr_ldap_search_alloc(TALLOC_CTX *ctx, char const *base_dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Allocate a new search object.
Definition: base.c:972
unlang_action_t fr_ldap_trunk_search(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *base_dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async search LDAP query on a trunk connection.
Definition: base.c:694
fr_table_num_sorted_t const fr_ldap_scope[]
Definition: base.c:69
#define PERROR(_fmt,...)
Definition: log.h:228
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
#define DEBUG4(_fmt,...)
Definition: log.h:267
#define RPERROR(fmt,...)
Definition: log.h:302
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
Definition: map.c:1015
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition: map.c:1783
talloc_free(reap)
Stores all information relating to an event list.
Definition: event.c:411
int map_proc_register(void *mod_inst, char const *name, map_proc_func_t evaluate, map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t literals_safe_for)
Register a map processor.
Definition: map_proc.c:111
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_BOOL
A truth value.
Definition: merged_model.c:95
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
long int ssize_t
Definition: merged_model.c:24
ssize_t xlat_eval(char *out, size_t outlen, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx)
Definition: merged_model.c:215
void * env_data
Per call environment data.
Definition: module_ctx.h:44
void * thread
Thread specific instance data.
Definition: module_ctx.h:43
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition: module_ctx.h:63
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
void * thread
Thread instance data.
Definition: module_ctx.h:62
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:59
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:58
Specifies a module method identifier.
Definition: module_method.c:36
module_thread_instance_t * module_rlm_thread_by_data(void const *data)
Definition: module_rlm.c:780
char const * section_type_value[MOD_COUNT]
Definition: module_rlm.c:64
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
static const conf_parser_t config[]
Definition: base.c:188
unlang_action_t rlm_ldap_map_profile(fr_ldap_result_code_t *ret, rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *dn, int scope, char const *filter, fr_ldap_map_exp_t const *expanded)
Search for and apply an LDAP profile.
Definition: profile.c:134
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG_ENABLED2()
Definition: radclient.h:50
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define RDEBUG(fmt,...)
Definition: radclient.h:53
#define DEBUG2(fmt,...)
Definition: radclient.h:43
static rs_t * conf
Definition: radsniff.c:53
#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
int fr_rb_flatten_inorder(TALLOC_CTX *ctx, void **out[], fr_rb_tree_t *tree)
static int8_t comp(void const *a, void const *b)
Definition: rbmonkey.c:13
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
#define RETURN_MODULE_INVALID
Definition: rcode.h:59
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition: rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition: rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition: rcode.h:44
static const call_env_method_t xlat_memberof_method_env
Definition: rlm_ldap.c:259
static int mod_detach(module_detach_ctx_t const *mctx)
Detach from the LDAP server and cleanup internal state.
Definition: rlm_ldap.c:2178
static int mod_load(void)
Definition: rlm_ldap.c:2676
static xlat_action_t ldap_profile_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Return whether evaluating the profile was successful.
Definition: rlm_ldap.c:986
map_list_t * profile_map
List of maps to apply to the profile.
Definition: rlm_ldap.c:72
#define REPEAT_LDAP_MEMBEROF_XLAT_RESULTS
Definition: rlm_ldap.c:808
fr_value_box_t user_base
Definition: rlm_ldap.c:55
LDAPMod mod_s[LDAP_MAX_ATTRMAP]
Definition: rlm_ldap.c:358
static unlang_action_t ldap_memberof_xlat_results(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Run the state machine for the LDAP membership xlat.
Definition: rlm_ldap.c:818
static conf_parser_t profile_config[]
Definition: rlm_ldap.c:87
fr_dict_attr_t const * attr_nt_password
Definition: rlm_ldap.c:315
static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void *mod_inst, UNUSED void *proc_inst, request_t *request, fr_value_box_list_t *url, map_list_t const *maps)
Perform a search and map the result of the search to server attributes.
Definition: rlm_ldap.c:1280
static void user_modify_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an in progress user modification.
Definition: rlm_ldap.c:1882
static void ldap_memberof_xlat_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an in-progress query for the LDAP group membership xlat.
Definition: rlm_ldap.c:799
static xlat_arg_parser_t const ldap_safe_xlat_arg[]
Definition: rlm_ldap.c:395
ldap_auth_call_env_t * call_env
Definition: rlm_ldap.c:346
static const call_env_method_t authenticate_method_env
Definition: rlm_ldap.c:184
static xlat_arg_parser_t const ldap_uri_escape_xlat_arg[]
Definition: rlm_ldap.c:390
fr_ldap_result_code_t ret
Definition: rlm_ldap.c:978
fr_dict_attr_t const * attr_ldap_userdn
Definition: rlm_ldap.c:314
global_lib_autoinst_t const * rlm_ldap_lib[]
Definition: rlm_ldap.c:333
static const call_env_method_t authorize_method_env
Definition: rlm_ldap.c:208
fr_value_box_t password
Definition: rlm_ldap.c:53
static unlang_action_t mod_map_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of an LDAP map query.
Definition: rlm_ldap.c:1162
static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule)
static xlat_arg_parser_t const ldap_uri_unescape_xlat_arg[]
Definition: rlm_ldap.c:450
static const call_env_method_t xlat_profile_method_env
Definition: rlm_ldap.c:285
#define CHECK_EXPANDED_SPACE(_expanded)
static int parse_sub_section(module_inst_ctx_t const *mctx, CONF_SECTION *parent, ldap_acct_section_t **config, rlm_components_t comp)
Parse an accounting sub section.
Definition: rlm_ldap.c:2199
map_list_t const * maps
Definition: rlm_ldap.c:367
static unlang_action_t user_modify_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Take the retrieved user DN and launch the async modification.
Definition: rlm_ldap.c:1923
fr_dict_attr_t const * attr_crypt_password
Definition: rlm_ldap.c:313
fr_value_box_t user_filter
Definition: rlm_ldap.c:65
static fr_dict_t const * dict_freeradius
Definition: rlm_ldap.c:303
static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, void const *data, UNUSED call_env_parser_t const *rule)
static void ldap_query_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Callback when LDAP query times out.
Definition: rlm_ldap.c:526
static int map_ctx_free(ldap_map_ctx_t *map_ctx)
Ensure map context is properly cleared up.
Definition: rlm_ldap.c:1255
static unlang_action_t mod_authenticate_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Perform async lookup of user DN if required for authentication.
Definition: rlm_ldap.c:1362
LDAPMod * mod_p[LDAP_MAX_ATTRMAP+1]
Definition: rlm_ldap.c:357
static conf_parser_t user_config[]
Definition: rlm_ldap.c:98
static int ldap_map_verify(CONF_SECTION *cs, UNUSED void *mod_inst, UNUSED void *proc_inst, tmpl_t const *src, UNUSED map_list_t const *maps)
Definition: rlm_ldap.c:1142
static fr_dict_attr_t const * attr_expr_bool_enum
Definition: rlm_ldap.c:317
static xlat_action_t ldap_memberof_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Process the results of evaluating LDAP group membership.
Definition: rlm_ldap.c:866
static fr_table_num_sorted_t const ldap_uri_scheme_table[]
Definition: rlm_ldap.c:379
static xlat_arg_parser_t const ldap_xlat_arg[]
Definition: rlm_ldap.c:634
static unlang_action_t mod_authorize_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Start LDAP authorization with async lookup of user DN.
Definition: rlm_ldap.c:1474
static unlang_action_t ldap_memberof_xlat_user_find(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
User object lookup as part of group membership xlat.
Definition: rlm_ldap.c:783
static int ldap_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
Escape function for a part of an LDAP URI.
Definition: rlm_ldap.c:496
fr_dict_attr_t const * attr_password
Definition: rlm_ldap.c:311
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_ldap.c:1423
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Bootstrap the module.
Definition: rlm_ldap.c:2296
#define REPEAT_MOD_AUTHORIZE_RESUME
Definition: rlm_ldap.c:1483
static const conf_parser_t acct_section_config[]
Definition: rlm_ldap.c:134
fr_value_box_t user_sasl_proxy
Definition: rlm_ldap.c:59
fr_ldap_thread_trunk_t * ttrunk
Definition: rlm_ldap.c:359
fr_value_box_t user_sasl_authname
Definition: rlm_ldap.c:58
fr_dict_attr_t const * attr_password_with_header
Definition: rlm_ldap.c:316
rlm_ldap_t const * inst
Definition: rlm_ldap.c:353
static conf_parser_t group_config[]
Definition: rlm_ldap.c:114
fr_ldap_query_t * query
Definition: rlm_ldap.c:369
static void mod_unload(void)
Definition: rlm_ldap.c:2697
fr_dict_attr_autoload_t rlm_ldap_dict_attr[]
Definition: rlm_ldap.c:320
ssize_t expect_password_offset
Definition: rlm_ldap.c:205
static unlang_action_t mod_accounting(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_ldap.c:2154
static int ldap_xlat_profile_ctx_free(ldap_xlat_profile_ctx_t *to_free)
Definition: rlm_ldap.c:999
fr_dict_attr_t const * attr_cleartext_password
Definition: rlm_ldap.c:312
tmpl_t const * password_tmpl
Definition: rlm_ldap.c:54
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_ldap.c:1803
static xlat_action_t ldap_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Callback when resuming after async ldap query is completed.
Definition: rlm_ldap.c:552
fr_value_box_t user_sasl_mech
Definition: rlm_ldap.c:57
static unlang_action_t mod_authenticate_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Initiate async LDAP bind to authenticate user.
Definition: rlm_ldap.c:1380
static unlang_action_t user_modify_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Perform async lookup of user DN if required for user modification.
Definition: rlm_ldap.c:1870
fr_dict_autoload_t rlm_ldap_dict[]
Definition: rlm_ldap.c:306
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Initialise thread specific data structure.
Definition: rlm_ldap.c:2233
module_rlm_t rlm_ldap
Definition: rlm_ldap.c:2706
char const * password
Definition: rlm_ldap.c:343
static int autz_ctx_free(ldap_autz_ctx_t *autz_ctx)
Ensure authorization context is properly cleared up.
Definition: rlm_ldap.c:1796
ldap_schemes_t
Definition: rlm_ldap.c:373
@ LDAP_SCHEME_UNIX
Definition: rlm_ldap.c:374
@ LDAP_SCHEME_TCP_SSL
Definition: rlm_ldap.c:376
@ LDAP_SCHEME_TCP
Definition: rlm_ldap.c:375
LDAPURLDesc * url
Definition: rlm_ldap.c:979
fr_value_box_t user_base
Definition: rlm_ldap.c:64
fr_ldap_map_exp_t expanded
Definition: rlm_ldap.c:980
static void ldap_xlat_signal(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
Callback for signalling async ldap query.
Definition: rlm_ldap.c:599
fr_ldap_query_t * query
Definition: rlm_ldap.c:360
static xlat_arg_parser_t const ldap_memberof_xlat_arg[]
Definition: rlm_ldap.c:879
char * passed[LDAP_MAX_ATTRMAP *2]
Definition: rlm_ldap.c:356
fr_value_box_t user_filter
Definition: rlm_ldap.c:56
static char * host_uri_canonify(request_t *request, LDAPURLDesc *url_parsed, fr_value_box_t *url_in)
Produce canonical LDAP host URI for finding trunks.
Definition: rlm_ldap.c:643
fr_ldap_thread_t * thread
Definition: rlm_ldap.c:345
static size_t ldap_uri_scheme_table_len
Definition: rlm_ldap.c:384
char const * dn
Definition: rlm_ldap.c:355
#define LDAP_URI_SAFE_FOR
This is the common function that actually ends up doing all the URI escaping.
Definition: rlm_ldap.c:388
static fr_uri_part_t const ldap_dn_parts[]
Definition: rlm_ldap.c:629
static unlang_action_t user_modify_final(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Handle results of user modification.
Definition: rlm_ldap.c:1894
static unlang_action_t user_modify(rlm_rcode_t *p_result, rlm_ldap_t const *inst, request_t *request, ldap_acct_section_t *section, ldap_usermod_call_env_t *call_env)
Modify user's object in LDAP.
Definition: rlm_ldap.c:1959
static const conf_parser_t module_config[]
Definition: rlm_ldap.c:139
static const call_env_method_t usermod_method_env
Definition: rlm_ldap.c:246
#define USER_CALL_ENV_COMMON(_struct)
Definition: rlm_ldap.c:180
ldap_usermod_call_env_t * call_env
Definition: rlm_ldap.c:354
static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Resume function called after each potential yield in LDAP authorization.
Definition: rlm_ldap.c:1502
static void mod_authorize_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Clear up when cancelling a mod_authorize call.
Definition: rlm_ldap.c:1786
fr_value_box_t profile_filter
Filter to use when searching for users.
Definition: rlm_ldap.c:71
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Clean up thread specific data structure.
Definition: rlm_ldap.c:2272
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
Definition: rlm_ldap.c:2483
fr_ldap_map_exp_t expanded
Definition: rlm_ldap.c:370
LDAPURLDesc * ldap_url
Definition: rlm_ldap.c:368
static const call_env_parser_t sasl_call_env[]
Definition: rlm_ldap.c:79
fr_value_box_t user_sasl_realm
Definition: rlm_ldap.c:60
static fr_uri_part_t const ldap_uri_parts[]
Definition: rlm_ldap.c:617
char const * dn
Definition: rlm_ldap.c:342
static unlang_action_t mod_post_auth(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_ldap.c:2164
rlm_ldap_t const * inst
Definition: rlm_ldap.c:344
Holds state of in progress async authentication.
Definition: rlm_ldap.c:341
Holds state of in progress LDAP map.
Definition: rlm_ldap.c:366
Parameters to allow ldap_update_section_parse to be reused.
Definition: rlm_ldap.c:203
Holds state of in progress ldap user modifications.
Definition: rlm_ldap.c:352
Call environment used in the profile xlat.
Definition: rlm_ldap.c:70
LDAP authorization and authentication module headers.
fr_ldap_map_exp_t expanded
Definition: rlm_ldap.h:194
ldap_group_xlat_status_t status
Definition: rlm_ldap.h:229
ldap_autz_call_env_t * call_env
Definition: rlm_ldap.h:197
struct berval ** profile_values
Definition: rlm_ldap.h:200
@ LDAP_ACCESS_SUSPENDED
User account has been suspended.
Definition: rlm_ldap.h:185
@ LDAP_ACCESS_ALLOWED
User is allowed to login.
Definition: rlm_ldap.h:183
@ LDAP_ACCESS_DISALLOWED
User it not allow to login (disabled)
Definition: rlm_ldap.h:184
fr_ldap_thread_trunk_t * ttrunk
Definition: rlm_ldap.h:196
rlm_ldap_t const * inst
Definition: rlm_ldap.h:193
fr_value_box_t profile_filter
Filter to use when searching for profiles.
Definition: rlm_ldap.h:144
fr_value_box_t user_filter
Filter to use when searching for users.
Definition: rlm_ldap.h:137
LDAPMessage * entry
Definition: rlm_ldap.h:198
@ LDAP_AUTZ_GROUP
Definition: rlm_ldap.h:167
@ LDAP_AUTZ_FIND
Definition: rlm_ldap.h:166
@ LDAP_AUTZ_DEFAULT_PROFILE
Definition: rlm_ldap.h:174
@ LDAP_AUTZ_USER_PROFILE
Definition: rlm_ldap.h:176
@ LDAP_AUTZ_MAP
Definition: rlm_ldap.h:173
@ LDAP_AUTZ_POST_DEFAULT_PROFILE
Definition: rlm_ldap.h:175
@ LDAP_AUTZ_POST_GROUP
Definition: rlm_ldap.h:168
ldap_autz_status_t status
Definition: rlm_ldap.h:199
fr_ldap_query_t * query
Definition: rlm_ldap.h:195
char const * attrs[2]
Definition: rlm_ldap.h:224
char * profile_value
Definition: rlm_ldap.h:202
fr_value_box_t user_base
Base DN in which to search for users.
Definition: rlm_ldap.h:156
ldap_access_state_t access_state
What state a user's account is in.
Definition: rlm_ldap.h:204
fr_value_box_t user_base
Base DN in which to search for users.
Definition: rlm_ldap.h:136
fr_value_box_t * filter
Definition: rlm_ldap.h:225
ldap_xlat_memberof_call_env_t * env_data
Definition: rlm_ldap.h:221
rlm_ldap_t const * inst
Definition: rlm_ldap.h:219
unlang_action_t rlm_ldap_find_user_async(TALLOC_CTX *ctx, rlm_ldap_t const *inst, request_t *request, fr_value_box_t *base, fr_value_box_t *filter_box, fr_ldap_thread_trunk_t *ttrunk, char const *attrs[], fr_ldap_query_t **query_out)
Initiate asynchronous retrieval of the DN of a user object.
Definition: user.c:155
char const * dn
Definition: rlm_ldap.h:203
map_list_t * user_map
Attribute map applied to users and profiles.
Definition: rlm_ldap.h:146
fr_value_box_t * basedn
Definition: rlm_ldap.h:226
void rlm_ldap_check_reply(request_t *request, rlm_ldap_t const *inst, char const *inst_name, bool expect_password, fr_ldap_thread_trunk_t const *ttrunk)
Verify we got a password from the search.
Definition: user.c:247
fr_value_box_t const * expect_password
True if the user_map included a mapping between an LDAP attribute and one of our password reference a...
Definition: rlm_ldap.h:148
fr_ldap_thread_trunk_t * ttrunk
Definition: rlm_ldap.h:227
static char const * rlm_find_user_dn_cached(request_t *request)
Definition: rlm_ldap.h:246
char const * reference
Configuration reference string.
Definition: rlm_ldap.h:23
CONF_SECTION * cs
Section configuration.
Definition: rlm_ldap.h:21
fr_value_box_t user_filter
Filter to use when searching for users.
Definition: rlm_ldap.h:157
fr_value_box_t default_profile
If this is set, we will search for a profile object with this name, and map any attributes it contain...
Definition: rlm_ldap.h:140
@ GROUP_XLAT_MEMB_FILTER
Definition: rlm_ldap.h:211
@ GROUP_XLAT_MEMB_ATTR
Definition: rlm_ldap.h:212
@ GROUP_XLAT_FIND_USER
Definition: rlm_ldap.h:210
dl_module_inst_t const * dlinst
Definition: rlm_ldap.h:192
fr_ldap_query_t * query
Definition: rlm_ldap.h:228
ldap_access_state_t rlm_ldap_check_access(rlm_ldap_t const *inst, request_t *request, LDAPMessage *entry)
Check for presence of access attribute in result.
Definition: user.c:195
Call environment used in LDAP authorization.
Definition: rlm_ldap.h:135
Holds state of in progress async authorization.
Definition: rlm_ldap.h:191
Holds state of in progress group membership check xlat.
Definition: rlm_ldap.h:218
Call environment used in group membership xlat.
Definition: rlm_ldap.h:155
static char const * name
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
unlang_action_t fr_ldap_sasl_bind_auth_async(request_t *request, fr_ldap_thread_t *thread, char const *mechs, char const *identity, char const *password, char const *proxy, char const *realm)
Initiate an async SASL LDAP bind for authentication.
Definition: sasl.c:504
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
Trim a talloced sbuff to the minimum length required to represent the contained string.
Definition: sbuff.c:399
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition: sbuff.h:167
#define fr_sbuff_buff(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition: sbuff.h:114
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
static tmpl_attr_t const * tmpl_attr_tail(tmpl_t const *vpt)
Return the last attribute reference.
Definition: tmpl.h:785
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
fr_signal_t
Definition: signal.h:48
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
return count
Definition: module.c:175
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
Value pair map.
Definition: map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
An element in a list of nested attribute references.
Definition: tmpl.h:427
fr_dict_attr_t const *_CONST da
Resolved dictionary attribute.
Definition: tmpl.h:431
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition: table.h:134
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:253
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
char * talloc_typed_strdup_buffer(TALLOC_CTX *ctx, char const *p)
Call talloc_strndup, setting the type on the new chunk correctly.
Definition: talloc.c:357
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
#define talloc_get_type_abort_const
Definition: talloc.h:270
"server local" time.
Definition: time.h:69
@ TMPL_ESCAPE_PRE_CONCAT
Pre-concatenation escaping is useful for DSLs where elements of the expansion are static,...
Definition: tmpl_escape.h:61
fr_table_num_ordered_t const fr_tokens_table[]
Definition: token.c:33
enum fr_token fr_token_t
@ T_OP_SUB_EQ
Definition: token.h:70
@ T_SINGLE_QUOTED_STRING
Definition: token.h:122
@ T_BARE_WORD
Definition: token.h:120
@ T_BACK_QUOTED_STRING
Definition: token.h:123
@ T_OP_SET
Definition: token.h:84
@ T_OP_ADD_EQ
Definition: token.h:69
@ T_OP_CMP_FALSE
Definition: token.h:105
@ T_DOUBLE_QUOTED_STRING
Definition: token.h:121
@ T_OP_INCRM
Definition: token.h:113
conf_parser_t const fr_trunk_config[]
Config parser definitions to populate a fr_trunk_conf_t.
Definition: trunk.c:306
void fr_trunk_request_signal_cancel(fr_trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2047
fr_trunk_enqueue_t fr_trunk_request_enqueue(fr_trunk_request_t **treq_out, fr_trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition: trunk.c:2481
Wraps a normal request.
Definition: trunk.c:97
@ FR_TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition: trunk.h:149
@ FR_TRUNK_ENQUEUE_OK
Operation was successful.
Definition: trunk.h:150
static fr_event_list_t * el
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition: xlat.c:561
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
xlat_action_t xlat_transparent(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition: xlat.h:365
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition: xlat.h:40
@ XLAT_ACTION_PUSH_UNLANG
An xlat function pushed an unlang frame onto the unlang stack.
Definition: xlat.h:37
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, request_t *request, char const *fmt, xlat_escape_legacy_t escape, void const *escape_ctx))
Definition: xlat_eval.c:1470
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx)
Parse a list of value boxes representing a URI.
Definition: uri.c:140
int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def)
Searches for a matching scheme in the table of schemes, using a list of value boxes representing the ...
Definition: uri.c:167
#define XLAT_URI_PART_TERMINATOR
Definition: uri.h:66
char const * name
Name of this part of the URI.
Definition: uri.h:47
Definition for a single part of a URI.
Definition: uri.h:46
static fr_slen_t parent
Definition: pair.h:844
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition: strerror.h:84
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:3689
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition: value.c:3630
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition: value.c:3985
int fr_value_box_bstr_realloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition: value.c:4053
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition: value.c:4097
int fr_value_box_bstrdup_buffer_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a talloced buffer containing a nul terminated string to a box, but don't copy it.
Definition: value.c:4202
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition: value.c:5725
@ FR_VALUE_BOX_LIST_FREE
Definition: value.h:214
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition: value.h:608
static fr_slen_t data
Definition: value.h:1259
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition: value.h:1042
#define FR_MAX_STRING_LEN
Definition: value.h:30
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition: value.h:155
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:619
static size_t char ** out
Definition: value.h:984
void * rctx
Resume context.
Definition: xlat_ctx.h:47
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:45
void * env_data
Expanded call env data.
Definition: xlat_ctx.h:46
An xlat calling ctx.
Definition: xlat_ctx.h:42
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition: xlat_func.c:415
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:360
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition: xlat_func.c:195
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition: xlat_func.c:405
int xlat_func_mono_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the argument of an xlat.
Definition: xlat_func.c:392
void xlat_func_unregister(char const *name)
Unregister an xlat function.
Definition: xlat_func.c:531
xlat_t * xlat_func_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function for a module.
Definition: xlat_func.c:274
#define xlat_func_safe_for_set(_xlat, _escaped)
Set the escaped values for output boxes.
Definition: xlat_func.h:80
@ XLAT_FUNC_FLAG_PURE
Definition: xlat_func.h:38