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