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