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