The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rlm_ldap.c
Go to the documentation of this file.
1/*
2 * This program 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: c0a37651a589e441a9d60cd7ef4e3d84b933e523 $
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: c0a37651a589e441a9d60cd7ef4e3d84b933e523 $")
31
33
34#include <freeradius-devel/util/debug.h>
35#include <freeradius-devel/util/table.h>
36#include <freeradius-devel/util/uri.h>
37
38#include <freeradius-devel/ldap/conf.h>
39
40#include <freeradius-devel/server/map_proc.h>
41#include <freeradius-devel/server/module_rlm.h>
42
43#include <freeradius-devel/unlang/xlat_func.h>
44#include <freeradius-devel/unlang/map.h>
45
46#include "rlm_ldap.h"
47
53
62
63typedef struct {
64 char const *attr;
66 tmpl_t const *tmpl;
73
74/** Call environment used in the profile xlat
75 */
76typedef struct {
77 fr_value_box_t profile_filter; //!< Filter to use when searching for users.
78 map_list_t *profile_map; //!< List of maps to apply to the profile.
80
81static 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);
82static 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);
83
84static 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);
85
93
100
102 { FR_CONF_OFFSET("scope", rlm_ldap_t, profile.obj_scope), .dflt = "base",
103 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = fr_ldap_scope, .len = &fr_ldap_scope_len } },
104 { FR_CONF_OFFSET("search_mode", rlm_ldap_t, profile.search_mode), .dflt = "auto",
106 { FR_CONF_DEPRECATED("attribute", rlm_ldap_t, user.profile_attr) },
107 { FR_CONF_DEPRECATED("attribute_suspend", rlm_ldap_t, user.profile_attr_suspend) },
108 { FR_CONF_OFFSET("check_attribute", rlm_ldap_t, profile.check_attr) },
109 { FR_CONF_OFFSET("sort_by", rlm_ldap_t, profile.obj_sort_by) },
110 { FR_CONF_OFFSET("fallthrough_attribute", rlm_ldap_t, profile.fallthrough_attr) },
111 { FR_CONF_OFFSET("fallthrough_default", rlm_ldap_t, profile.fallthrough_def), .dflt = "yes" },
113};
114
115/*
116 * User configuration
117 */
119 { FR_CONF_OFFSET("scope", rlm_ldap_t, user.obj_scope), .dflt = "sub",
120 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = fr_ldap_scope, .len = &fr_ldap_scope_len } },
121 { FR_CONF_OFFSET("sort_by", rlm_ldap_t, user.obj_sort_by) },
122
123 { FR_CONF_OFFSET("access_attribute", rlm_ldap_t, user.obj_access_attr) },
124 { FR_CONF_OFFSET("access_positive", rlm_ldap_t, user.access_positive), .dflt = "yes" },
125 { FR_CONF_OFFSET("access_value_negate", rlm_ldap_t, user.access_value_negate), .dflt = "false" },
126 { FR_CONF_OFFSET("access_value_suspend", rlm_ldap_t, user.access_value_suspend), .dflt = "suspended" },
127 { FR_CONF_OFFSET("dn_attribute", rlm_ldap_t, user.dn_attr_str), .dflt = "LDAP-UserDN" },
128 { FR_CONF_OFFSET_IS_SET("expect_password", FR_TYPE_BOOL, 0, rlm_ldap_t, user.expect_password) },
129 { FR_CONF_OFFSET("profile_attribute", rlm_ldap_t, user.profile_attr) },
130 { FR_CONF_OFFSET("profile_attribute_suspend", rlm_ldap_t, user.profile_attr_suspend) },
132};
133
134/*
135 * Group configuration
136 */
138 { FR_CONF_OFFSET("filter", rlm_ldap_t, group.obj_filter) },
139 { FR_CONF_OFFSET("scope", rlm_ldap_t, group.obj_scope), .dflt = "sub",
140 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = fr_ldap_scope, .len = &fr_ldap_scope_len } },
141
142 { FR_CONF_OFFSET("name_attribute", rlm_ldap_t, group.obj_name_attr), .dflt = "cn" },
143 { FR_CONF_OFFSET("membership_attribute", rlm_ldap_t, group.userobj_membership_attr) },
144 { FR_CONF_OFFSET_FLAGS("membership_filter", CONF_FLAG_XLAT, rlm_ldap_t, group.obj_membership_filter) },
145 { FR_CONF_OFFSET("cacheable_name", rlm_ldap_t, group.cacheable_name), .dflt = "no" },
146 { FR_CONF_OFFSET("cacheable_dn", rlm_ldap_t, group.cacheable_dn), .dflt = "no" },
147 { FR_CONF_OFFSET("cache_attribute", rlm_ldap_t, group.cache_attr_str) },
148 { FR_CONF_OFFSET("group_attribute", rlm_ldap_t, group.attribute) },
149 { FR_CONF_OFFSET("allow_dangling_group_ref", rlm_ldap_t, group.allow_dangling_refs), .dflt = "no" },
150 { FR_CONF_OFFSET("skip_on_suspend", rlm_ldap_t, group.skip_on_suspend), .dflt = "yes"},
151 { FR_CONF_OFFSET("profile_attribute", rlm_ldap_t, group.profile_attr) },
152 { FR_CONF_OFFSET("profile_attribute_suspend", rlm_ldap_t, group.profile_attr_suspend) },
154};
155
156static const conf_parser_t module_config[] = {
157 /*
158 * Pool config items
159 */
160 { FR_CONF_OFFSET_FLAGS("server", CONF_FLAG_MULTI, rlm_ldap_t, handle_config.server_str) }, /* Do not set to required */
161
162 /*
163 * Common LDAP conf parsers
164 */
166
167 { FR_CONF_OFFSET("valuepair_attribute", rlm_ldap_t, valuepair_attr) },
168
169 { FR_CONF_OFFSET("dn_attribute", rlm_ldap_t, dn_attr) },
170
171#ifdef LDAP_CONTROL_X_SESSION_TRACKING
172 { FR_CONF_OFFSET("session_tracking", rlm_ldap_t, session_tracking), .dflt = "no" },
173#endif
174
175#ifdef WITH_EDIR
176 /* support for eDirectory Universal Password */
177 { FR_CONF_OFFSET("edir", rlm_ldap_t, edir) }, /* NULL defaults to "no" */
178
179 /*
180 * Attempt to bind with the cleartext password we got from eDirectory
181 * Universal password for additional authorization checks.
182 */
183 { FR_CONF_OFFSET("edir_autz", rlm_ldap_t, edir_autz) }, /* NULL defaults to "no" */
184#endif
185
186 { FR_CONF_POINTER("user", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) user_config },
187
188 { FR_CONF_POINTER("group", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) group_config },
189
190 { FR_CONF_POINTER("profile", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) profile_config },
191
192 { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_ldap_t, trunk_conf, trunk_config ) },
193
194 { FR_CONF_OFFSET_SUBSECTION("bind_pool", 0, rlm_ldap_t, bind_trunk_conf, trunk_config ) },
195
197};
198
199#define LDAP_DN_SAFE_FOR (fr_value_box_safe_for_t)fr_ldap_dn_escape_func
200#define LDAP_FILTER_SAFE_FOR (fr_value_box_safe_for_t)fr_ldap_filter_escape_func
201
202#define LDAP_DN_CALL_ENV_ESCAPE \
203 .pair.escape = { \
204 .box_escape = { \
205 .func = fr_ldap_dn_box_escape, \
206 .safe_for = LDAP_DN_SAFE_FOR, \
207 .always_escape = false, \
208 }, \
209 .mode = TMPL_ESCAPE_PRE_CONCAT \
210 }, \
211 .pair.literals_safe_for = LDAP_DN_SAFE_FOR
212
213#define LDAP_FILTER_CALL_ENV_ESCAPE \
214 .pair.escape = { \
215 .box_escape = { \
216 .func = fr_ldap_filter_box_escape, \
217 .safe_for = LDAP_FILTER_SAFE_FOR, \
218 .always_escape = false, \
219 }, \
220 .mode = TMPL_ESCAPE_PRE_CONCAT \
221 }, \
222 .pair.literals_safe_for = LDAP_FILTER_SAFE_FOR
223
224#define USER_CALL_ENV_COMMON(_struct) \
225 { FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, _struct, user_base), \
226 .pair.dflt = "", .pair.dflt_quote = T_SINGLE_QUOTED_STRING, LDAP_DN_CALL_ENV_ESCAPE }, \
227 { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, _struct, user_filter), \
228 .pair.dflt = "(&)", .pair.dflt_quote = T_SINGLE_QUOTED_STRING, LDAP_FILTER_CALL_ENV_ESCAPE }
229
245
246/** Parameters to allow ldap_update_section_parse to be reused
247 */
252
255 .env = (call_env_parser_t[]) {
258 .map_offset = offsetof(ldap_autz_call_env_t, user_map),
259 .expect_password_offset = offsetof(ldap_autz_call_env_t, expect_password)
260 } },
262 ((call_env_parser_t[]) {
265 })) },
267 ((call_env_parser_t[]) {
271 .pair.func = ldap_group_filter_parse,
273 },
275 })) },
277 ((call_env_parser_t[]) {
283 .pair.dflt = "(&)", .pair.dflt_quote = T_SINGLE_QUOTED_STRING,
286 } )) },
288 }
289};
290
291#define USERMOD_ENV(_section) static const call_env_method_t _section ## _usermod_method_env = { \
292 FR_CALL_ENV_METHOD_OUT(ldap_usermod_call_env_t), \
293 .env = (call_env_parser_t[]) { \
294 { FR_CALL_ENV_SUBSECTION("user", NULL, CALL_ENV_FLAG_REQUIRED, \
295 ((call_env_parser_t[]) { \
296 USER_CALL_ENV_COMMON(ldap_usermod_call_env_t), CALL_ENV_TERMINATOR \
297 })) }, \
298 { FR_CALL_ENV_SUBSECTION_FUNC(STRINGIFY(_section), CF_IDENT_ANY, CALL_ENV_FLAG_SUBSECTION | CALL_ENV_FLAG_PARSE_MISSING, ldap_mod_section_parse) }, \
299 CALL_ENV_TERMINATOR \
300 } \
301}
302
303USERMOD_ENV(accounting);
305
327
330 .env = (call_env_parser_t[]) {
333 .map_offset = offsetof(ldap_xlat_profile_call_env_t, profile_map),
334 .expect_password_offset = -1
335 } },
337 ((call_env_parser_t[]) {
339 .pair.dflt = "(&)", .pair.dflt_quote = T_SINGLE_QUOTED_STRING,
340 LDAP_FILTER_CALL_ENV_ESCAPE }, //!< Correct filter for when the DN is known.
342 })) },
344 }
345};
346
348
351 { .out = &dict_freeradius, .proto = "freeradius" },
353};
354
361
364 { .out = &attr_password, .name = "Password", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
365 { .out = &attr_cleartext_password, .name = "Password.Cleartext", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
366 { .out = &attr_crypt_password, .name = "Password.Crypt", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
367 { .out = &attr_nt_password, .name = "Password.NT", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
368 { .out = &attr_password_with_header, .name = "Password.With-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
369 { .out = &attr_expr_bool_enum, .name = "Expr-Bool-Enum", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
370
372};
373
374extern global_lib_autoinst_t const *rlm_ldap_lib[];
379
380/** Holds state of in progress async authentication
381 *
382 */
390
391/** Holds state of in progress ldap user modifications
392 *
393 */
407
408/** Holds state of in progress LDAP map
409 *
410 */
411typedef struct {
412 map_list_t const *maps;
413 LDAPURLDesc *ldap_url;
416 LDAPControl *serverctrls[LDAP_MAX_CONTROLS];
418
424
426 { L("ldap://"), LDAP_SCHEME_UNIX },
427 { L("ldapi://"), LDAP_SCHEME_TCP },
428 { L("ldaps://"), LDAP_SCHEME_TCP_SSL },
429};
431
436
438 { .required = true, .concat = true, .type = FR_TYPE_STRING },
440};
441
442/** Escape a string for use in an RFC 4514 DN attribute value
443 *
444 * @ingroup xlat_functions
445 */
447 UNUSED xlat_ctx_t const *xctx,
448 request_t *request, fr_value_box_list_t *in)
449{
450 fr_value_box_t *vb, *in_vb, *in_group = fr_value_box_list_head(in);
451 fr_sbuff_t sbuff;
452 fr_sbuff_uctx_talloc_t sbuff_ctx;
453 size_t len;
454
455 fr_assert(in_group->type == FR_TYPE_GROUP);
456
457 while ((in_vb = fr_value_box_list_pop_head(&in_group->vb_group))) {
459 fr_dcursor_append(out, in_vb);
460 continue;
461 }
462
463 MEM(vb = fr_value_box_alloc_null(ctx));
464
465 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, in_vb->vb_length * 3, in_vb->vb_length * 3)) {
466 REDEBUG("Failed to allocate buffer for escaped string");
467 talloc_free(vb);
468 return XLAT_ACTION_FAIL;
469 }
470
471 len = fr_ldap_dn_escape_func(request, fr_sbuff_buff(&sbuff), in_vb->vb_length * 3 + 1, in_vb->vb_strvalue, NULL);
472
473 fr_sbuff_trim_talloc(&sbuff, len);
474 fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), in_vb->tainted);
475 talloc_free(in_vb);
476
478 }
479 return XLAT_ACTION_DONE;
480}
481
482/** Escape a string for use as an RFC 4515 filter assertion value
483 *
484 * @ingroup xlat_functions
485 */
487 UNUSED xlat_ctx_t const *xctx,
488 request_t *request, fr_value_box_list_t *in)
489{
490 fr_value_box_t *vb, *in_vb, *in_group = fr_value_box_list_head(in);
491 fr_sbuff_t sbuff;
492 fr_sbuff_uctx_talloc_t sbuff_ctx;
493 size_t len;
494
495 fr_assert(in_group->type == FR_TYPE_GROUP);
496
497 while ((in_vb = fr_value_box_list_pop_head(&in_group->vb_group))) {
499 fr_dcursor_append(out, in_vb);
500 continue;
501 }
502
503 MEM(vb = fr_value_box_alloc_null(ctx));
504
505 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, in_vb->vb_length * 3, in_vb->vb_length * 3)) {
506 REDEBUG("Failed to allocate buffer for escaped string");
507 talloc_free(vb);
508 return XLAT_ACTION_FAIL;
509 }
510
511 len = fr_ldap_filter_escape_func(request, fr_sbuff_buff(&sbuff), in_vb->vb_length * 3 + 1, in_vb->vb_strvalue, NULL);
512
513 fr_sbuff_trim_talloc(&sbuff, len);
514 fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), in_vb->tainted);
515 talloc_free(in_vb);
516
518 }
519 return XLAT_ACTION_DONE;
520}
521
526
527/** Unescape LDAP string
528 *
529 * @ingroup xlat_functions
530 */
532 UNUSED xlat_ctx_t const *xctx,
533 request_t *request, fr_value_box_list_t *in)
534{
535 fr_value_box_t *vb, *in_vb = NULL, *in_group = fr_value_box_list_head(in);
536 fr_sbuff_t sbuff;
537 fr_sbuff_uctx_talloc_t sbuff_ctx;
538 size_t len;
539
540 fr_assert(in_group->type == FR_TYPE_GROUP);
541
542 while ((in_vb = fr_value_box_list_next(&in_group->vb_group, in_vb))) {
543
544 MEM(vb = fr_value_box_alloc_null(ctx));
545 /*
546 * Maximum space needed for output will be the same as the input
547 */
548 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, in_vb->vb_length, in_vb->vb_length)) {
549 REDEBUG("Failed to allocate buffer for unescaped string");
550 talloc_free(vb);
551 return XLAT_ACTION_FAIL;
552 }
553
554 /*
555 * Call the unescape function, including the space for the trailing NULL
556 */
557 len = fr_ldap_uri_unescape_func(request, fr_sbuff_buff(&sbuff), in_vb->vb_length + 1, in_vb->vb_strvalue, NULL);
558
559 /*
560 * Trim buffer to fit used space and assign to box
561 */
562 fr_sbuff_trim_talloc(&sbuff, len);
563 fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), in_vb->tainted);
565 }
566
567 return XLAT_ACTION_DONE;
568}
569
570/** Escape function for a part of an LDAP URI
571 *
572 */
573static int ldap_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
574{
575 fr_sbuff_t sbuff;
576 fr_sbuff_uctx_talloc_t sbuff_ctx;
577 size_t len;
578
579 /*
580 * Maximum space needed for output would be 3 times the input if every
581 * char needed escaping
582 */
583 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->vb_length * 3, vb->vb_length * 3)) {
584 fr_strerror_printf_push("Failed to allocate buffer for escaped argument");
585 return -1;
586 }
587
588 /*
589 * Call the escape function, including the space for the trailing NULL
590 */
591 len = fr_ldap_dn_escape_func(NULL, fr_sbuff_buff(&sbuff), vb->vb_length * 3 + 1, vb->vb_strvalue, NULL);
592
593 fr_sbuff_trim_talloc(&sbuff, len);
595
596 return 0;
597}
598
599/** Callback when LDAP query times out
600 *
601 */
603{
604 fr_ldap_query_t *query = talloc_get_type_abort(uctx, fr_ldap_query_t);
605 trunk_request_t *treq;
606 request_t *request;
607
608 /*
609 * If the trunk request has completed but the query
610 * has not yet resumed, query->treq will be NULL
611 */
612 if (!query->treq) return;
613
614 treq = talloc_get_type_abort(query->treq, trunk_request_t);
615 request = treq->request;
616
617 ROPTIONAL(RERROR, ERROR, "Timeout waiting for LDAP query");
618
620
621 query->ret = LDAP_RESULT_TIMEOUT;
623}
624
626 { .required = true, .concat = true, .type = FR_TYPE_STRING },
627 { .required = true, .concat = true, .type = FR_TYPE_STRING },
629};
630
631/** Modify an LDAP URI to append an option to all attributes
632 *
633 * This is for the corner case where a URI is provided by a third party system
634 * and needs amending before being used. e.g. a CRL distribution point extracted
635 * from a certificate may need the "binary" option appending to the attribute
636 * being requested.
637 *
638 * @ingroup xlat_functions
639 */
641 request_t *request, fr_value_box_list_t *in)
642{
643 fr_value_box_t *uri, *option_vb;
644 char *attrs_fixed, **attr, port[6];
645 char const *option;
646 LDAPURLDesc *ldap_url;
647 fr_value_box_t *vb;
648 int ret;
649
650 XLAT_ARGS(in, &uri, &option_vb);
651
652#ifdef STATIC_ANALYZER
653 if (!option_vb) return XLAT_ACTION_FAIL;
654#endif
655
656 if (option_vb->vb_length < 1) {
657 RERROR("LDAP attriubte option must not be blank");
658 return XLAT_ACTION_FAIL;
659 }
660
661 if (!ldap_is_ldap_url(uri->vb_strvalue)) {
662 REDEBUG("String passed does not look like an LDAP URL");
663 return XLAT_ACTION_FAIL;
664 }
665
666 ret = ldap_url_parse(uri->vb_strvalue, &ldap_url);
667 if (ret != LDAP_URL_SUCCESS){
668 RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ret));
669 return XLAT_ACTION_FAIL;
670 }
671
672 /*
673 * No attributes, just return what was presented.
674 */
675 if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] || !*ldap_url->lud_attrs[0]) {
676 xlat_arg_copy_out(ctx, out, in, uri);
677 goto done;
678 }
679
680 if (option_vb->vb_strvalue[0] != ';') {
681 option = talloc_asprintf(option_vb, ";%s", option_vb->vb_strvalue);
682 } else {
683 option = option_vb->vb_strvalue;
684 }
685
686 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
687 attrs_fixed = talloc_strdup(vb, "");
688
689 attr = ldap_url->lud_attrs;
690 while (*attr) {
691 attrs_fixed = talloc_strdup_append(attrs_fixed, *attr);
692 if (!strstr(*attr, option)) attrs_fixed = talloc_strdup_append(attrs_fixed, option);
693 attr++;
694 if (*attr) attrs_fixed = talloc_strdup_append(attrs_fixed, ",");
695 }
696
697 snprintf(port, sizeof(port), "%d", ldap_url->lud_port);
698 fr_value_box_asprintf(vb, vb, NULL, uri->tainted, "%s://%s%s%s/%s?%s?%s?%s",
699 ldap_url->lud_scheme,
700 ldap_url->lud_host ? ldap_url->lud_host : "",
701 ldap_url->lud_host ? ":" : "",
702 ldap_url->lud_host ? port : "",
703 ldap_url->lud_dn, attrs_fixed,
704 fr_table_str_by_value(fr_ldap_scope, ldap_url->lud_scope, ""),
705 ldap_url->lud_filter ? ldap_url->lud_filter : "");
706
708done:
709 ldap_free_urldesc(ldap_url);
710 return XLAT_ACTION_DONE;
711}
712
713/** Callback when resuming after async ldap query is completed
714 *
715 */
717 xlat_ctx_t const *xctx,
718 request_t *request, UNUSED fr_value_box_list_t *in)
719{
720 fr_ldap_query_t *query = talloc_get_type_abort(xctx->rctx, fr_ldap_query_t);
721 fr_ldap_connection_t *ldap_conn = query->ldap_conn;
722 fr_value_box_t *vb = NULL;
723 LDAPMessage *msg;
724 struct berval **values;
725 char const **attr;
726 int count, i;
727
728 if (query->ret != LDAP_RESULT_SUCCESS) return XLAT_ACTION_FAIL;
729
730 /*
731 * We only parse "entries"
732 */
733 for (msg = ldap_first_entry(ldap_conn->handle, query->result); msg; msg = ldap_next_entry(ldap_conn->handle, msg)) {
734 for (attr = query->search.attrs; *attr; attr++) {
735 values = ldap_get_values_len(ldap_conn->handle, msg, *attr);
736 if (!values) {
737 RDEBUG2("No \"%s\" attributes found in specified object", *attr);
738 continue;
739 }
740
741 count = ldap_count_values_len(values);
742 for (i = 0; i < count; i++) {
743 MEM(vb = fr_value_box_alloc_null(ctx));
744 if (fr_value_box_bstrndup(vb, vb, NULL, values[i]->bv_val, values[i]->bv_len, true) < 0) {
745 talloc_free(vb);
746 RPERROR("Failed creating value from LDAP response");
747 break;
748 }
750 }
751 ldap_value_free_len(values);
752 }
753 }
754
755 talloc_free(query);
756
757 return XLAT_ACTION_DONE;
758}
759
760/** Callback for signalling async ldap query
761 *
762 */
763static void ldap_xlat_signal(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
764{
765 fr_ldap_query_t *query = talloc_get_type_abort(xctx->rctx, fr_ldap_query_t);
766
767 if (!query->treq) return;
768
769 RDEBUG2("Forcefully cancelling pending LDAP query");
770
772}
773
774/*
775 * If a part doesn't have an escaping function, parsing will fail unless the input
776 * was marked up with a safe_for value by the ldap arg parsing, i.e. was a literal
777 * input argument to the xlat.
778 *
779 * This is equivalent to the old "tainted_allowed" flag.
780 */
782 { .name = "scheme", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 }, .extra_skip = 2 },
783 { .name = "host", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 } },
784 { .name = "port", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 } },
785 { .name = "dn", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = ldap_uri_part_escape },
786 { .name = "attrs", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }},
787 { .name = "scope", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = ldap_uri_part_escape },
788 { .name = "filter", .safe_for = LDAP_DN_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1}, .func = ldap_uri_part_escape },
789 { .name = "exts", .safe_for = LDAP_DN_SAFE_FOR, .func = ldap_uri_part_escape },
791};
792
793static fr_uri_part_t const ldap_dn_parts[] = {
794 { .name = "dn", .safe_for = LDAP_DN_SAFE_FOR , .func = ldap_uri_part_escape },
796};
797
799 { .required = true, .type = FR_TYPE_STRING, .safe_for = LDAP_DN_SAFE_FOR, .will_escape = true, },
801};
802
803/** Produce canonical LDAP host URI for finding trunks
804 *
805 */
806static inline CC_HINT(always_inline)
807char *host_uri_canonify(request_t *request, LDAPURLDesc *url_parsed, fr_value_box_t *url_in)
808{
809 char *host;
810
811 LDAPURLDesc tmp_desc = {
812 .lud_scheme = url_parsed->lud_scheme,
813 .lud_host = url_parsed->lud_host,
814 .lud_port = url_parsed->lud_port,
815 .lud_scope = -1
816 };
817 host = ldap_url_desc2str(&tmp_desc);
818 if (unlikely(host == NULL)) REDEBUG("Invalid LDAP URL - %pV", url_in); \
819
820 return host;
821}
822
823/** Utility function for parsing LDAP URLs
824 *
825 * All LDAP xlat functions that work with LDAP URLs should call this function to parse the URL.
826 *
827 * @param[out] uri_parsed LDAP URL parsed. Must be freed with ldap_url_desc_free.
828 * @param[out] host_out host name to use for the query. Must be freed with ldap_mem_free
829 * if free_host_out is true.
830 * @param[out] free_host_out True if host_out should be freed.
831 * @param[in] request Request being processed.
832 * @param[in] host_default Default host to use if the URL does not specify a host.
833 * @param[in] uri_in URI to parse.
834 * @return
835 * - 0 on success.
836 * - -1 on failure.
837 */
838static int ldap_xlat_uri_parse(LDAPURLDesc **uri_parsed, char **host_out, bool *free_host_out,
839 request_t *request, char *host_default, fr_value_box_t *uri_in)
840{
841 fr_value_box_t *uri;
842 int ldap_url_ret;
843
844 *free_host_out = false;
845
846 if (fr_uri_escape_list(&uri_in->vb_group, ldap_uri_parts, NULL) < 0){
847 RPERROR("Failed to escape LDAP URI");
848 error:
849 *uri_parsed = NULL;
850 return -1;
851 }
852
853 /*
854 * Smush everything into the first URI box
855 */
856 uri = fr_value_box_list_head(&uri_in->vb_group);
857
858 if (fr_value_box_list_concat_in_place(uri, uri, &uri_in->vb_group,
859 FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
860 RPEDEBUG("Failed concatenating input");
861 goto error;
862 }
863
864 if (!ldap_is_ldap_url(uri->vb_strvalue)) {
865 REDEBUG("String passed does not look like an LDAP URL");
866 goto error;
867 }
868
869 ldap_url_ret = ldap_url_parse(uri->vb_strvalue, uri_parsed);
870 if (ldap_url_ret != LDAP_URL_SUCCESS){
871 RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
872 goto error;
873 }
874
875 /*
876 * If the URL is <scheme>:/// the parsed host will be NULL - use config default
877 */
878 if (!(*uri_parsed)->lud_host) {
879 *host_out = host_default;
880 } else {
881 *host_out = host_uri_canonify(request, *uri_parsed, uri);
882 if (unlikely(*host_out == NULL)) {
883 ldap_free_urldesc(*uri_parsed);
884 *uri_parsed = NULL;
885 return -1;
886 }
887 *free_host_out = true;
888 }
889
890 return 0;
891}
892
893/** Expand an LDAP URL into a query, and return a string result from that query.
894 *
895 * @ingroup xlat_functions
896 */
898 xlat_ctx_t const *xctx,
899 request_t *request, fr_value_box_list_t *in)
900{
901 fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
902 fr_value_box_t *uri;
903 char *host;
904 bool free_host = false;
905 fr_ldap_config_t const *handle_config = t->config;
907 fr_ldap_query_t *query = NULL;
908
909 LDAPURLDesc *ldap_url;
910
911 XLAT_ARGS(in, &uri);
912
913 if (ldap_xlat_uri_parse(&ldap_url, &host, &free_host, request, handle_config->server, uri) < 0) return XLAT_ACTION_FAIL;
914
915 /*
916 * Nothing, empty string, "*" string, or got 2 things, die.
917 */
918 if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] || !*ldap_url->lud_attrs[0] ||
919 (strcmp(ldap_url->lud_attrs[0], "*") == 0) || ldap_url->lud_attrs[1]) {
920 REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
921 ldap_free_urldesc(ldap_url);
922 return XLAT_ACTION_FAIL;
923 }
924
926 ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
927 (char const * const*)ldap_url->lud_attrs, NULL, NULL);
928 query->ldap_url = ldap_url; /* query destructor will free URL */
929
930 if (ldap_url->lud_exts) {
931 LDAPControl *serverctrls[LDAP_MAX_CONTROLS];
932 int i;
933
934 serverctrls[0] = NULL;
935
936 if (fr_ldap_parse_url_extensions(serverctrls, NUM_ELEMENTS(serverctrls),
937 query->ldap_url->lud_exts) < 0) {
938 RPERROR("Parsing URL extensions failed");
939 if (free_host) ldap_memfree(host);
940
941 query_error:
942 talloc_free(query);
943 return XLAT_ACTION_FAIL;
944 }
945
946 for (i = 0; i < LDAP_MAX_CONTROLS; i++) {
947 if (!serverctrls[i]) break;
948 query->serverctrls[i].control = serverctrls[i];
949 query->serverctrls[i].freeit = true;
950 }
951 }
952
953 /*
954 * Figure out what trunked connection we can use
955 * to communicate with the host.
956 *
957 * If free_host is true, we must free the host
958 * after deciding on a trunk connection as it
959 * was allocated by host_uri_canonify.
960 */
961 ttrunk = fr_thread_ldap_trunk_get(t, host, handle_config->admin_identity,
962 handle_config->admin_password, request, handle_config);
963 if (free_host) ldap_memfree(host);
964 if (!ttrunk) {
965 REDEBUG("Unable to get LDAP query for xlat");
966 goto query_error;
967 }
968
969 switch (trunk_request_enqueue(&query->treq, ttrunk->trunk, request, query, NULL)) {
970 case TRUNK_ENQUEUE_OK:
972 break;
973
974 default:
975 REDEBUG("Unable to enqueue LDAP query for xlat");
976 goto query_error;
977 }
978
979 if (fr_timer_in(query, unlang_interpret_event_list(request)->tl, &query->ev, handle_config->res_timeout,
980 false, ldap_query_timeout, query) < 0) {
981 REDEBUG("Unable to set timeout for LDAP query");
983 goto query_error;
984 }
985
987}
988
989/** User object lookup as part of group membership xlat
990 *
991 * Called if the ldap membership xlat is used and the user DN is not already known
992 */
994{
995 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(uctx, ldap_group_xlat_ctx_t);
996
997 if (xlat_ctx->env_data->user_filter.type == FR_TYPE_STRING) xlat_ctx->filter = &xlat_ctx->env_data->user_filter;
998
999 xlat_ctx->basedn = &xlat_ctx->env_data->user_base;
1000
1002 /* discard, this function is only used by xlats */NULL,
1003 xlat_ctx->inst, request,
1004 xlat_ctx->basedn, xlat_ctx->filter,
1005 xlat_ctx->ttrunk, xlat_ctx->attrs, &xlat_ctx->query);
1006}
1007
1008/** Cancel an in-progress query for the LDAP group membership xlat
1009 *
1010 */
1011static void ldap_group_xlat_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
1012{
1013 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(uctx, ldap_group_xlat_ctx_t);
1014
1015 if (!xlat_ctx->query || !xlat_ctx->query->treq) return;
1016
1018}
1019
1020#define REPEAT_LDAP_MEMBEROF_XLAT_RESULTS \
1021 if (unlang_function_repeat_set(request, ldap_group_xlat_results) < 0) do { \
1022 RETURN_UNLANG_FAIL; \
1023 } while (0)
1024
1025/** Run the state machine for the LDAP membership xlat
1026 *
1027 * This is called after each async lookup is completed
1028 *
1029 * Will stop early, and set p_result to unlang_result
1030 */
1032{
1033 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(uctx, ldap_group_xlat_ctx_t);
1034 rlm_ldap_t const *inst = xlat_ctx->inst;
1035
1036 /*
1037 * Check to see if rlm_ldap_check_groupobj_dynamic or rlm_ldap_check_userobj_dynamic failed
1038 */
1039 if (p_result->rcode == RLM_MODULE_FAIL) return UNLANG_ACTION_CALCULATE_RESULT;
1040
1041 switch (xlat_ctx->status) {
1043 if (!xlat_ctx->dn) xlat_ctx->dn = rlm_find_user_dn_cached(inst, request);
1044 if (!xlat_ctx->dn) RETURN_UNLANG_FAIL;
1045
1046 RDEBUG3("Entered GROUP_XLAT_FIND_USER with user DN \"%s\"", xlat_ctx->dn);
1047 if (inst->group.obj_membership_filter) {
1049 RDEBUG3("Checking for user in group objects");
1053 }
1054 }
1056
1058 if (xlat_ctx->found) RETURN_UNLANG_OK;
1059
1060 RDEBUG3("Entered GROUP_XLAT_MEMB_FILTER with user DN \"%s\"", xlat_ctx->dn);
1061 if (inst->group.userobj_membership_attr) {
1066 }
1067 }
1069
1071 RDEBUG3("Entered GROUP_XLAT_MEMB_ATTR with user DN \"%s\"", xlat_ctx->dn);
1072 if (xlat_ctx->found) RETURN_UNLANG_OK;
1073 break;
1074 }
1075
1077}
1078
1079/** Process the results of evaluating LDAP group membership
1080 *
1081 */
1083 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1084{
1085 ldap_group_xlat_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, ldap_group_xlat_ctx_t);
1086 fr_value_box_t *vb;
1087
1089 vb->vb_bool = xlat_ctx->found;
1091
1092 return XLAT_ACTION_DONE;
1093}
1094
1096 { .required = true, .concat = true, .type = FR_TYPE_STRING, .safe_for = LDAP_DN_SAFE_FOR },
1098};
1099
1100/** Check for a user being in a LDAP group
1101 *
1102 * @ingroup xlat_functions
1103 */
1104static xlat_action_t ldap_group_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
1105 request_t *request, fr_value_box_list_t *in)
1106{
1107 fr_value_box_t *vb = NULL, *group_vb = fr_value_box_list_pop_head(in);
1109 fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
1110 ldap_xlat_memberof_call_env_t *env_data = talloc_get_type_abort(xctx->env_data, ldap_xlat_memberof_call_env_t);
1111 bool group_is_dn;
1113
1114 RDEBUG2("Searching for user in group \"%pV\"", group_vb);
1115
1116 if (group_vb->vb_length == 0) {
1117 REDEBUG("Cannot do comparison (group name is empty)");
1118 return XLAT_ACTION_FAIL;
1119 }
1120
1121 group_is_dn = fr_ldap_util_is_dn(group_vb->vb_strvalue, group_vb->vb_length);
1122 if (group_is_dn) {
1123 char *norm;
1124 size_t len;
1125
1126 MEM(norm = talloc_array(group_vb, char, talloc_array_length(group_vb->vb_strvalue)));
1127 len = fr_ldap_util_normalise_dn(norm, group_vb->vb_strvalue);
1128
1129 /*
1130 * Will clear existing buffer (i.e. group_vb->vb_strvalue)
1131 */
1132 fr_value_box_bstrdup_buffer_shallow(group_vb, group_vb, NULL, norm, group_vb->tainted);
1133
1134 /*
1135 * Trim buffer to match normalised DN
1136 */
1137 fr_value_box_bstr_realloc(group_vb, NULL, group_vb, len);
1138 }
1139
1140 if ((group_is_dn && inst->group.cacheable_dn) || (!group_is_dn && inst->group.cacheable_name)) {
1141 unlang_result_t our_result;
1142
1143 rlm_ldap_check_cached(&our_result, inst, request, group_vb);
1144 switch (our_result.rcode) {
1146 RDEBUG2("User is not a member of \"%pV\"", group_vb);
1147 return XLAT_ACTION_DONE;
1148
1149 case RLM_MODULE_OK:
1150 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
1151 vb->vb_bool = true;
1153 return XLAT_ACTION_DONE;
1154
1155 /*
1156 * Fallback to dynamic search
1157 */
1158 default:
1159 break;
1160 }
1161 }
1162
1164
1166 .inst = inst,
1167 .group = group_vb,
1168 .dn = rlm_find_user_dn_cached(inst, request),
1169 .attrs = { inst->group.userobj_membership_attr, NULL },
1170 .group_is_dn = group_is_dn,
1171 .env_data = env_data
1172 };
1173
1174 xlat_ctx->ttrunk = fr_thread_ldap_trunk_get(t, inst->handle_config.server, inst->handle_config.admin_identity,
1175 inst->handle_config.admin_password, request, &inst->handle_config);
1176
1177 if (!xlat_ctx->ttrunk) {
1178 REDEBUG("Unable to get LDAP trunk for group membership check");
1179 error:
1181 return XLAT_ACTION_FAIL;
1182 }
1183
1184 if (unlang_xlat_yield(request, ldap_group_xlat_resume, NULL, 0, xlat_ctx) != XLAT_ACTION_YIELD) goto error;
1185
1187 request,
1190 ldap_group_xlat_cancel, ~FR_SIGNAL_CANCEL,
1192 xlat_ctx) < 0) goto error;
1193
1195}
1196
1203
1204/** Return whether evaluating the profile was successful
1205 *
1206 */
1208 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1209{
1210 ldap_xlat_profile_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, ldap_xlat_profile_ctx_t);
1211 fr_value_box_t *vb;
1212
1214 vb->vb_bool = (xlat_ctx->ret == LDAP_RESULT_SUCCESS) && (xlat_ctx->applied > 0);
1216
1217 return XLAT_ACTION_DONE;
1218}
1219
1221{
1222 if (to_free->url) {
1223 ldap_free_urldesc(to_free->url);
1224 to_free->url = NULL;
1225 }
1226 return 0;
1227}
1228
1229/** Expand an LDAP URL into a query, applying the results using the user update map.
1230 *
1231 * For fetching profiles by DN.
1232 *
1233 * @ingroup xlat_functions
1234 */
1236 xlat_ctx_t const *xctx,
1237 request_t *request, fr_value_box_list_t *in)
1238{
1240 fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
1241 ldap_xlat_profile_call_env_t *env_data = talloc_get_type_abort(xctx->env_data, ldap_xlat_profile_call_env_t);
1242 fr_value_box_t *uri_components, *uri;
1243 char *host_url, *host = NULL;
1244 fr_ldap_config_t const *handle_config = t->config;
1245 fr_ldap_thread_trunk_t *ttrunk;
1247
1248 int ldap_url_ret;
1249
1250 char const *dn;
1251 char const *filter;
1252 int scope;
1253
1254 bool is_dn;
1255
1256 XLAT_ARGS(in, &uri_components);
1257
1258 is_dn = (fr_uri_has_scheme(&uri_components->vb_group, ldap_uri_scheme_table, ldap_uri_scheme_table_len, -1) < 0);
1259
1260 /*
1261 * Apply different escaping rules based on whether the first
1262 * arg lookgs like a URI or a DN.
1263 */
1264 if (is_dn) {
1265 if (fr_uri_escape_list(&uri_components->vb_group, ldap_dn_parts, NULL) < 0) {
1266 RPERROR("Failed to escape LDAP profile DN");
1267 return XLAT_ACTION_FAIL;
1268 }
1269 } else {
1270 if (fr_uri_escape_list(&uri_components->vb_group, ldap_uri_parts, NULL) < 0) {
1271 RPERROR("Failed to escape LDAP profile URI");
1272 return XLAT_ACTION_FAIL;
1273 }
1274 }
1275
1276 /*
1277 * Smush everything into the first URI box
1278 */
1279 uri = fr_value_box_list_head(&uri_components->vb_group);
1280 if (fr_value_box_list_concat_in_place(uri, uri, &uri_components->vb_group,
1281 FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
1282 RPEDEBUG("Failed concatenating input");
1283 return XLAT_ACTION_FAIL;
1284 }
1285
1286 /*
1287 * Allocate a resumption context to store temporary resource and results
1288 */
1290 talloc_set_destructor(xlat_ctx, ldap_xlat_profile_ctx_free);
1291
1292 if (is_dn) {
1293 host_url = handle_config->server;
1294 dn = talloc_typed_strdup_buffer(xlat_ctx, uri->vb_strvalue);
1295 filter = env_data->profile_filter.vb_strvalue;
1296 scope = inst->profile.obj_scope;
1297 } else {
1298 ldap_url_ret = ldap_url_parse(uri->vb_strvalue, &xlat_ctx->url);
1299 if (ldap_url_ret != LDAP_URL_SUCCESS){
1300 RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
1301 error:
1303 return XLAT_ACTION_FAIL;
1304 }
1305
1306 /*
1307 * The URL must specify a DN
1308 */
1309 if (!xlat_ctx->url->lud_dn) {
1310 REDEBUG("LDAP URI must specify a profile DN");
1311 goto error;
1312 }
1313
1314 dn = xlat_ctx->url->lud_dn;
1315 /*
1316 * Either we use the filter from the URL or we use the default filter
1317 * configured for profiles.
1318 */
1319 filter = xlat_ctx->url->lud_filter ? xlat_ctx->url->lud_filter : env_data->profile_filter.vb_strvalue;
1320
1321 /*
1322 * Determine if the URL includes a scope.
1323 */
1324 scope = xlat_ctx->url->lud_scope == LDAP_SCOPE_DEFAULT ? inst->profile.obj_scope : xlat_ctx->url->lud_scope;
1325
1326 /*
1327 * If the URL is <scheme>:/// the parsed host will be NULL - use config default
1328 */
1329 if (!xlat_ctx->url->lud_host) {
1330 host_url = handle_config->server;
1331 } else {
1332 host_url = host = host_uri_canonify(request, xlat_ctx->url, uri);
1333 if (unlikely(host_url == NULL)) goto error;
1334 }
1335 }
1336
1337 /*
1338 * Synchronous expansion of maps (fixme!)
1339 */
1340 if (fr_ldap_map_expand(xlat_ctx, &xlat_ctx->expanded, request, env_data->profile_map,
1341 inst->valuepair_attr, inst->profile.check_attr, inst->profile.fallthrough_attr) < 0) goto error;
1342 ttrunk = fr_thread_ldap_trunk_get(t, host_url, handle_config->admin_identity,
1343 handle_config->admin_password, request, handle_config);
1344 if (host) ldap_memfree(host);
1345 if (!ttrunk) {
1346 REDEBUG("Unable to get LDAP query for xlat");
1347 goto error;
1348 }
1349
1350 if (unlang_xlat_yield(request, ldap_profile_xlat_resume, NULL, 0, xlat_ctx) != XLAT_ACTION_YIELD) goto error;
1351
1352 /*
1353 * Pushes a frame onto the stack to retrieve and evaluate a profile
1354 */
1355 if (rlm_ldap_map_profile(&xlat_ctx->ret, &xlat_ctx->applied, inst, request, ttrunk, dn,
1356 scope, filter, &xlat_ctx->expanded) < 0) goto error;
1357
1359}
1360
1361/** State of an in progress whoami extended operation
1362 *
1363 */
1364typedef struct {
1365 fr_ldap_query_t *query; //!< Current query performing the whoami operation.
1367
1368/** Return the authorization identity from the whoami response
1369 *
1370 * @ingroup xlat_functions
1371 */
1373 request_t *request, UNUSED fr_value_box_list_t *in)
1374{
1375 ldap_xlat_whoami_ctx_t *xlat_ctx = talloc_get_type_abort(xctx->rctx, ldap_xlat_whoami_ctx_t);
1376 fr_ldap_query_t *query = xlat_ctx->query;
1377 fr_value_box_t *vb;
1378 struct berval *authz_id = NULL;
1379 char const *p;
1380 size_t len;
1381 int err;
1382
1383 if (query->ret != LDAP_RESULT_SUCCESS) {
1384 REDEBUG("Whoami extended operation failed");
1385 return XLAT_ACTION_FAIL;
1386 }
1387
1388 err = ldap_parse_extended_result(query->ldap_conn->handle, query->result, NULL, &authz_id, false);
1389 if (err != LDAP_SUCCESS) {
1390 REDEBUG("Failed parsing whoami response: %s", ldap_err2string(err));
1391 return XLAT_ACTION_FAIL;
1392 }
1393
1394 /*
1395 * An empty authzId means the connection is bound anonymously.
1396 */
1397 if (!authz_id || (authz_id->bv_len == 0)) {
1398 if (authz_id) ber_bvfree(authz_id);
1399 return XLAT_ACTION_DONE;
1400 }
1401
1402 /*
1403 * RFC 4532 returns an authzId (RFC 4513), "dn:<dn>" for
1404 * distinguished names. Strip the prefix, "u:<user>" forms
1405 * are returned unmodified.
1406 */
1407 p = authz_id->bv_val;
1408 len = authz_id->bv_len;
1409 if ((len >= 3) && (memcmp(p, "dn:", 3) == 0)) {
1410 p += 3;
1411 len -= 3;
1412 }
1413
1414 MEM(vb = fr_value_box_alloc_null(ctx));
1415 MEM(fr_value_box_bstrndup(vb, vb, NULL, p, len, false) == 0);
1417
1418 ber_bvfree(authz_id);
1419
1420 return XLAT_ACTION_DONE;
1421}
1422
1423/** Perform the RFC 4532 whoami extended operation
1424 *
1425 * Runs on the same connections as the module's queries, so the identity
1426 * returned is the one the directory resolved for the admin bind.
1427 *
1428 * @ingroup xlat_functions
1429 */
1431 xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
1432{
1433 fr_ldap_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
1434 fr_ldap_config_t const *handle_config = t->config;
1435 fr_ldap_thread_trunk_t *ttrunk;
1437
1438 ttrunk = fr_thread_ldap_trunk_get(t, handle_config->server, handle_config->admin_identity,
1439 handle_config->admin_password, request, handle_config);
1440 if (!ttrunk) {
1441 REDEBUG("Unable to get LDAP trunk for whoami");
1442 return XLAT_ACTION_FAIL;
1443 }
1444
1446
1448 error:
1450 return XLAT_ACTION_FAIL;
1451 }
1452
1453 if (fr_ldap_trunk_extended(xlat_ctx, &xlat_ctx->query, request, ttrunk,
1454 LDAP_EXOP_WHO_AM_I, NULL, NULL, NULL) != UNLANG_ACTION_PUSHED_CHILD) goto error;
1455
1457}
1458
1459/*
1460 * Verify the result of the map.
1461 */
1462static int ldap_map_verify(CONF_SECTION *cs, UNUSED void const *mod_inst, UNUSED void *proc_inst,
1463 tmpl_t const *src, UNUSED map_list_t const *maps)
1464{
1465 if (!src) {
1466 cf_log_err(cs, "Missing LDAP URI");
1467
1468 return -1;
1469 }
1470
1471 return 0;
1472}
1473
1474/** Process the results of an LDAP map query
1475 *
1476 * @param[out] p_result Result of map expansion:
1477 * - #RLM_MODULE_NOOP no rows were returned.
1478 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
1479 * - #RLM_MODULE_FAIL if an error occurred.
1480 * @param[in] mpctx module map ctx.
1481 * @param[in,out] request The current request.
1482 * @param[in] url LDAP url specifying base DN and filter.
1483 * @param[in] maps Head of the map list.
1484 * @return One of UNLANG_ACTION_*
1485 */
1486static unlang_action_t mod_map_resume(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
1487 UNUSED fr_value_box_list_t *url, UNUSED map_list_t const *maps)
1488{
1489 ldap_map_ctx_t *map_ctx = talloc_get_type_abort(mpctx->rctx, ldap_map_ctx_t);
1490 fr_ldap_query_t *query = map_ctx->query;
1491 fr_ldap_map_exp_t *expanded = &map_ctx->expanded;
1493 LDAPMessage *entry;
1494 map_t const *map;
1495
1496 switch (query->ret) {
1498 rcode = RLM_MODULE_UPDATED;
1499 break;
1500
1502 case LDAP_RESULT_BAD_DN:
1503 goto finish;
1504
1506 goto finish;
1507
1508 default:
1509 rcode = RLM_MODULE_FAIL;
1510 goto finish;
1511 }
1512
1513 for (entry = ldap_first_entry(query->ldap_conn->handle, query->result);
1514 entry;
1515 entry = ldap_next_entry(query->ldap_conn->handle, entry)) {
1516 char *dn = NULL;
1517 int i;
1518
1519 if (RDEBUG_ENABLED2) {
1520 dn = ldap_get_dn(query->ldap_conn->handle, entry);
1521 RDEBUG2("Processing \"%s\"", dn);
1522 }
1523
1524 RINDENT();
1525 for (map = map_list_head(map_ctx->maps), i = 0;
1526 map != NULL;
1527 map = map_list_next(map_ctx->maps, map), i++) {
1529 int ret, iter_err = 0;
1530
1531 if (!fr_ldap_value_iter_alloc(&iter_err, &iter, request, query->ldap_conn->handle, entry,
1532 expanded->attrs[i])) {
1533 talloc_free(iter);
1534 if (unlikely(iter_err < 0)) {
1535 RPERROR("Failed parsing entry");
1536 rcode = RLM_MODULE_FAIL;
1537 ldap_memfree(dn);
1538 goto finish;
1539 }
1540
1541 /*
1542 * Many LDAP directories don't expose the DN of
1543 * the object as an attribute, so we need this
1544 * hack, to allow the user to retrieve it.
1545 */
1546 if (strcmp(LDAP_VIRTUAL_DN_ATTR, expanded->attrs[i]) == 0) {
1547 if (!dn) dn = ldap_get_dn(query->ldap_conn->handle, entry);
1548
1549 ret = map_to_request(request, map, fr_ldap_map_getdn, dn);
1550 if (ret == -1) {
1551 rcode = RLM_MODULE_FAIL;
1552 ldap_memfree(dn);
1553 goto finish;
1554 }
1555 continue;
1556 }
1557
1558 RDEBUG3("Attribute \"%s\" not found in LDAP object", expanded->attrs[i]);
1559
1560 continue;
1561 }
1562
1563 ret = map_to_request(request, map, fr_ldap_map_getvalue, iter);
1564 talloc_free(iter);
1565 if (ret == -1) {
1566 rcode = RLM_MODULE_FAIL;
1567 ldap_memfree(dn);
1568 goto finish;
1569 }
1570 }
1571 ldap_memfree(dn);
1572 REXDENT();
1573 }
1574
1575finish:
1576 RETURN_UNLANG_RCODE(rcode);
1577}
1578
1579/** Ensure map context is properly cleared up
1580 *
1581 */
1583{
1584 int i = 0;
1585 talloc_free(map_ctx->expanded.ctx);
1586 ldap_free_urldesc(map_ctx->ldap_url);
1587 while ((i < LDAP_MAX_CONTROLS) && map_ctx->serverctrls[i]) {
1588 ldap_control_free(map_ctx->serverctrls[i]);
1589 i++;
1590 }
1591 return (0);
1592}
1593
1594/** Perform a search and map the result of the search to server attributes
1595 *
1596 * Unlike LDAP xlat, this can be used to process attributes from multiple entries.
1597 *
1598 * @todo For xlat expansions we need to parse the raw URL first, and then apply
1599 * different escape functions to the different parts.
1600 *
1601 * @param[out] p_result Result of map expansion:
1602 * - #RLM_MODULE_NOOP no rows were returned.
1603 * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
1604 * - #RLM_MODULE_FAIL if an error occurred.
1605 * @param[in] mpctx module map ctx.
1606 * @param[in,out] request The current request.
1607 * @param[in] url LDAP url specifying base DN and filter.
1608 * @param[in] maps Head of the map list.
1609 * @return UNLANG_ACTION_CALCULATE_RESULT
1610 */
1611static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
1612 fr_value_box_list_t *url, map_list_t const *maps)
1613{
1615 fr_ldap_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, fr_ldap_thread_t);
1616
1617 LDAPURLDesc *ldap_url;
1618 int ldap_url_ret;
1619 fr_ldap_thread_trunk_t *ttrunk;
1620
1621 fr_value_box_t *url_head;
1623 char *host_url, *host = NULL;
1624
1625 if (fr_uri_escape_list(url, ldap_uri_parts, NULL) < 0) {
1626 RPERROR("Failed to escape LDAP map URI");
1628 }
1629
1630 url_head = fr_value_box_list_head(url);
1631 if (!url_head) {
1632 REDEBUG("LDAP URL cannot be empty");
1634 }
1635
1636 if (fr_value_box_list_concat_in_place(url_head, url_head, url, FR_TYPE_STRING,
1637 FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
1638 RPEDEBUG("Failed concatenating input");
1640 }
1641
1642 if (!ldap_is_ldap_url(url_head->vb_strvalue)) {
1643 REDEBUG("Map query string does not look like a valid LDAP URI");
1645 }
1646
1648 talloc_set_destructor(map_ctx, map_ctx_free);
1649 map_ctx->maps = maps;
1650
1651 ldap_url_ret = ldap_url_parse(url_head->vb_strvalue, &map_ctx->ldap_url);
1652 if (ldap_url_ret != LDAP_URL_SUCCESS){
1653 RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
1654 fail:
1657 }
1658 ldap_url = map_ctx->ldap_url;
1659
1660 if (ldap_url->lud_exts) {
1661 if (fr_ldap_parse_url_extensions(map_ctx->serverctrls, NUM_ELEMENTS(map_ctx->serverctrls),
1662 ldap_url->lud_exts) < 0) {
1663 RPERROR("Parsing URL extensions failed");
1664 goto fail;
1665 }
1666 }
1667
1668 /*
1669 * Expand the RHS of the maps to get the name of the attributes.
1670 */
1671 if (fr_ldap_map_expand(map_ctx, &map_ctx->expanded, request, maps, NULL, NULL, NULL) < 0) goto fail;
1672
1673 /*
1674 * If the URL is <scheme>:/// the parsed host will be NULL - use config default
1675 */
1676 if (!ldap_url->lud_host) {
1677 host_url = inst->handle_config.server;
1678 } else {
1679 host_url = host = host_uri_canonify(request, ldap_url, url_head);
1680 if (unlikely(host_url == NULL)) goto fail;
1681 }
1682
1683 ttrunk = fr_thread_ldap_trunk_get(thread, host_url, inst->handle_config.admin_identity,
1684 inst->handle_config.admin_password, request, &inst->handle_config);
1685 if (host) ldap_memfree(host);
1686 if (!ttrunk) goto fail;
1687
1688 if (unlikely(unlang_map_yield(request, mod_map_resume, NULL, 0, map_ctx) != UNLANG_ACTION_YIELD)) goto fail;
1689
1690 return fr_ldap_trunk_search(map_ctx, &map_ctx->query, request, ttrunk, ldap_url->lud_dn,
1691 ldap_url->lud_scope, ldap_url->lud_filter, map_ctx->expanded.attrs,
1692 map_ctx->serverctrls, NULL);
1693}
1694
1695static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1696{
1698 fr_ldap_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, fr_ldap_thread_t);
1699 ldap_auth_ctx_t *auth_ctx;
1700 ldap_auth_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_auth_call_env_t);
1701
1702 if (call_env->password.type != FR_TYPE_STRING) {
1703 RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
1704 RWDEBUG("without checking if %s is present", call_env->password_tmpl->name);
1705 RWDEBUG("*********************************************");
1706 RWDEBUG("* THAT CONFIGURATION IS WRONG. DELETE IT. ");
1707 RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
1708 RWDEBUG("*********************************************");
1709
1710 REDEBUG("Attribute \"%s\" is required for authentication", call_env->password_tmpl->name);
1712 }
1713
1714 auth_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_auth_ctx_t);
1715 *auth_ctx = (ldap_auth_ctx_t){
1716 .password = call_env->password.vb_strvalue,
1717 .thread = thread,
1718 .inst = inst,
1719 .call_env = call_env
1720 };
1721
1722 /*
1723 * Find the user's DN
1724 */
1725 auth_ctx->dn = rlm_find_user_dn_cached(inst, request);
1726
1727 /*
1728 * The DN is required for non-SASL auth
1729 */
1730 if (!auth_ctx->dn && (call_env->user_sasl_mech.type != FR_TYPE_STRING)) {
1731 REDEBUG("No DN found for authentication. Populate control.%s with the DN to use in authentication.",
1732 inst->user.da->name);
1733 REDEBUG("You should call %s in the recv section and check its return.", inst->mi->name);
1734 talloc_free(auth_ctx);
1736 }
1737
1738 /*
1739 * Log the password
1740 */
1741 if (RDEBUG_ENABLED3) {
1742 RDEBUG("Login attempt with password \"%pV\"", &call_env->password);
1743 } else {
1744 RDEBUG2("Login attempt with password");
1745 }
1746
1747 /*
1748 * SASL bind auth will have the mech set.
1749 */
1750 if (auth_ctx->call_env->user_sasl_mech.type == FR_TYPE_STRING) {
1751#ifdef WITH_SASL
1752 RDEBUG2("Login attempt using identity \"%pV\"", &call_env->user_sasl_authname);
1753
1754 return fr_ldap_sasl_bind_auth_async(p_result, request, auth_ctx->thread, call_env->user_sasl_mech.vb_strvalue,
1755 call_env->user_sasl_authname.vb_strvalue,
1756 auth_ctx->password, call_env->user_sasl_proxy.vb_strvalue,
1757 call_env->user_sasl_realm.vb_strvalue);
1758#else
1759 RDEBUG("Configuration item 'sasl.mech' is not supported. "
1760 "The linked version of libldap does not provide ldap_sasl_bind( function");
1762#endif
1763 }
1764
1765 RDEBUG2("Login attempt as \"%s\"", auth_ctx->dn);
1766
1767 return fr_ldap_bind_auth_async(p_result, request, auth_ctx->thread, auth_ctx->dn, auth_ctx->password);
1768}
1769
1770/** Remove duplicate DNs from a NULL terminated list, keeping the first occurrence
1771 *
1772 * A profile referenced by both a group object and the user object must
1773 * only be applied once.
1774 */
1775static void profile_dn_list_dedupe(char const **dn_list)
1776{
1777 int i, j, n;
1778
1779 for (n = 0; dn_list[n]; n++);
1780
1781 for (i = 1; i < n; i++) {
1782 for (j = 0; j < i; j++) {
1783 if (strcasecmp(dn_list[i], dn_list[j]) != 0) continue;
1784
1785 memmove(&dn_list[i], &dn_list[i + 1], (n - i) * sizeof(dn_list[0]));
1786 n--;
1787 i--;
1788 break;
1789 }
1790 }
1791}
1792
1793#define REPEAT_MOD_AUTHORIZE_RESUME \
1794 if (unlang_module_yield(request, mod_authorize_resume, NULL, 0, autz_ctx) == UNLANG_ACTION_FAIL) do { \
1795 p_result->rcode = RLM_MODULE_FAIL; \
1796 goto finish; \
1797 } while (0)
1798
1799/** Resume function called after each potential yield in LDAP authorization
1800 *
1801 * Some operations may or may not yield. E.g. if group membership is
1802 * read from an attribute returned with the user object and is already
1803 * in the correct form, that will not yield.
1804 * Hence, each state may fall through to the next.
1805 *
1806 * @param p_result Result of current authorization.
1807 * @param mctx Module context.
1808 * @param request Current request.
1809 * @return An rcode.
1810 */
1812{
1813 ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(mctx->rctx, ldap_autz_ctx_t);
1815 ldap_autz_call_env_t *call_env = talloc_get_type_abort(autz_ctx->call_env, ldap_autz_call_env_t);
1816 int ldap_errno;
1817 LDAP *handle = fr_ldap_handle_thread_local();
1819
1820 /*
1821 * If a previous async call returned one of the "failure" results just return.
1822 */
1823 switch (p_result->rcode) {
1824 case RLM_MODULE_REJECT:
1825 case RLM_MODULE_FAIL:
1826 case RLM_MODULE_HANDLED:
1827 case RLM_MODULE_INVALID:
1829 goto finish;
1830
1831 default:
1832 break;
1833 }
1834
1835 switch (autz_ctx->status) {
1836 case LDAP_AUTZ_FIND:
1837 /*
1838 * If a user entry has been found the current rcode will be OK
1839 */
1840 if (p_result->rcode != RLM_MODULE_OK) return UNLANG_ACTION_CALCULATE_RESULT;
1841
1842 autz_ctx->entry = ldap_first_entry(handle, autz_ctx->query->result);
1843 if (!autz_ctx->entry) {
1844 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1845 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1846
1847 goto finish;
1848 }
1849
1850 /*
1851 * Check for access.
1852 */
1853 if (inst->user.obj_access_attr) {
1854 autz_ctx->access_state = rlm_ldap_check_access(inst, request, autz_ctx->entry);
1855 switch (autz_ctx->access_state) {
1857 break;
1858
1860 if (inst->group.skip_on_suspend) goto post_group;
1861 break;
1862
1864 p_result->rcode = RLM_MODULE_DISALLOW;
1865 goto finish;
1866 }
1867 }
1868
1869 /*
1870 * Check if we need to cache group memberships,
1871 * or record group DNs for the group profile search.
1872 */
1873 if ((inst->group.cacheable_dn || inst->group.cacheable_name ||
1874 rlm_ldap_profile_attr_select(inst->group.profile_attr, inst->group.profile_attr_suspend,
1875 autz_ctx->access_state)) &&
1876 (inst->group.userobj_membership_attr)) {
1878 if (rlm_ldap_cacheable_userobj(p_result, request, autz_ctx,
1879 inst->group.userobj_membership_attr) == UNLANG_ACTION_PUSHED_CHILD) {
1880 autz_ctx->status = LDAP_AUTZ_GROUP;
1882 }
1883 if (p_result->rcode != RLM_MODULE_OK) goto finish;
1884 }
1886
1887 case LDAP_AUTZ_GROUP:
1888 if (inst->group.cacheable_dn || inst->group.cacheable_name ||
1889 rlm_ldap_profile_attr_select(inst->group.profile_attr, inst->group.profile_attr_suspend,
1890 autz_ctx->access_state)) {
1892 if (rlm_ldap_cacheable_groupobj(p_result, request, autz_ctx) == UNLANG_ACTION_PUSHED_CHILD) {
1893 autz_ctx->status = LDAP_AUTZ_GROUP_PROFILES;
1895 }
1896 if (p_result->rcode != RLM_MODULE_OK) goto finish;
1897 }
1899
1901 if (rlm_ldap_profile_attr_select(inst->group.profile_attr, inst->group.profile_attr_suspend,
1902 autz_ctx->access_state) &&
1903 autz_ctx->group_dn_list && (talloc_str_list_num(autz_ctx->group_dn_list) > 0)) {
1905 if (rlm_ldap_group_profiles(p_result, request, autz_ctx) == UNLANG_ACTION_PUSHED_CHILD) {
1906 autz_ctx->status = LDAP_AUTZ_POST_GROUP;
1908 }
1909 if (p_result->rcode != RLM_MODULE_OK) goto finish;
1910 }
1912
1914 post_group:
1915#ifdef WITH_EDIR
1916 /*
1917 * We already have a Password.Cleartext. Skip edir.
1918 */
1919 if (fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_cleartext_password)) goto skip_edir;
1920
1921 /*
1922 * Retrieve Universal Password if we use eDirectory
1923 */
1924 if (inst->edir) {
1925 autz_ctx->dn = rlm_find_user_dn_cached(inst, request);
1926
1927 /*
1928 * Retrieve universal password
1929 */
1931 autz_ctx->status = LDAP_AUTZ_EDIR_BIND;
1932 return fr_ldap_edir_get_password(p_result, request, autz_ctx->dn, autz_ctx->ttrunk,
1934 }
1936
1937 case LDAP_AUTZ_EDIR_BIND:
1938 if (inst->edir && inst->edir_autz) {
1939 fr_pair_t *password = fr_pair_find_by_da(&request->control_pairs,
1941 fr_ldap_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data,
1943
1944 if (!password) {
1945 REDEBUG("Failed to find control.Password.Cleartext");
1946 p_result->rcode = RLM_MODULE_FAIL;
1947 goto finish;
1948 }
1949
1950 RDEBUG2("Binding as %s for eDirectory authorization checks", autz_ctx->dn);
1951
1952 /*
1953 * Bind as the user
1954 */
1956 autz_ctx->status = LDAP_AUTZ_POST_EDIR;
1957 return fr_ldap_bind_auth_async(p_result, request, thread, autz_ctx->dn, password->vp_strvalue);
1958 }
1959 goto skip_edir;
1960
1961 case LDAP_AUTZ_POST_EDIR:
1962 {
1963 /*
1964 * The result of the eDirectory user bind will be in p_result.
1965 * Anything other than RLM_MODULE_OK is a failure.
1966 */
1967 break;
1968
1969 }
1971
1972#endif
1973 case LDAP_AUTZ_MAP:
1974#ifdef WITH_EDIR
1975 skip_edir:
1976#endif
1977 if (!map_list_empty(call_env->user_map) || inst->valuepair_attr) {
1978 RDEBUG2("Processing user attributes");
1979 RINDENT();
1980 if (fr_ldap_map_do(request, NULL, inst->valuepair_attr,
1981 &autz_ctx->expanded, autz_ctx->entry) > 0) autz_ctx->rcode = RLM_MODULE_UPDATED;
1982 REXDENT();
1983 rlm_ldap_check_reply(request, inst, autz_ctx->dlinst->name, call_env->expect_password->vb_bool, autz_ctx->ttrunk);
1984 }
1986
1987 case LDAP_AUTZ_PROFILES:
1988 {
1989 talloc_str_list_t *list;
1990 char const *profile_attr;
1991 char const **dn_p;
1992 bool have_default = !fr_box_is_null(&call_env->default_profile);
1993 size_t count = 0, strings_len = 0, group_count = 0, i;
1994
1995 /*
1996 * Which set of profiles to apply depends on the user's
1997 * access state. The default profile always applies.
1998 */
1999 profile_attr = rlm_ldap_profile_attr_select(inst->user.profile_attr, inst->user.profile_attr_suspend,
2000 autz_ctx->access_state);
2001 if (profile_attr) {
2002 if (unlikely(fr_ldap_result_values_len(&count, &strings_len, handle,
2003 autz_ctx->query->result, profile_attr) < 0)) {
2004 RPERROR("Failed parsing user object");
2005 p_result->rcode = RLM_MODULE_FAIL;
2006 goto finish;
2007 }
2008 if (count > 0) {
2009 RDEBUG2("Processing %zu profile(s) found in attribute \"%s\"", count, profile_attr);
2010 } else {
2011 RDEBUG2("No profile(s) found in attribute \"%s\"", profile_attr);
2012 }
2013 }
2014
2015 if (autz_ctx->group_profile_dn_list) {
2016 group_count = talloc_str_list_num(autz_ctx->group_profile_dn_list);
2017 RDEBUG2("Processing %zu profile(s) found in group objects", group_count);
2018 }
2019
2020 if (!have_default && (group_count == 0) && (count == 0)) break;
2021
2022 /*
2023 * Build the list of profile DNs to apply, the default
2024 * profile first, then the profiles from the group
2025 * objects, then the profiles from the user object.
2026 */
2027 MEM(list = fr_ldap_str_list_afrom_result(autz_ctx, handle, autz_ctx->query->result,
2028 count ? profile_attr : NULL,
2029 (have_default ? 1 : 0) + group_count));
2030 dn_p = list->strings;
2031 if (have_default) *dn_p++ = call_env->default_profile.vb_strvalue;
2032 for (i = 0; i < group_count; i++) *dn_p++ = autz_ctx->group_profile_dn_list->strings[i];
2033
2034 /*
2035 * Evaluate the child object below each profile instead
2036 * of the profile itself.
2037 */
2038 if (!fr_box_is_null(&call_env->profile_child_rdn) && (call_env->profile_child_rdn.vb_length > 0)) {
2039 for (dn_p = list->strings; *dn_p; dn_p++) {
2040 MEM(*dn_p = talloc_asprintf(list, "%s,%s",
2041 call_env->profile_child_rdn.vb_strvalue, *dn_p));
2042 }
2043 }
2044
2045 autz_ctx->profile_dn_list = list->strings;
2047
2048 if (!autz_ctx->profile_dn_list[0]) break;
2049
2050 if (RDEBUG_ENABLED3) {
2051 for (dn_p = autz_ctx->profile_dn_list; *dn_p; dn_p++) {
2052 RDEBUG3("Will evaluate profile with DN \"%s\"", *dn_p);
2053 }
2054 }
2055
2057 ret = rlm_ldap_map_profiles(NULL, &autz_ctx->profiles_applied, inst, request, autz_ctx->ttrunk,
2058 autz_ctx->profile_dn_list, call_env->profile_filter.vb_strvalue,
2059 &autz_ctx->expanded);
2060 switch (ret) {
2061 case UNLANG_ACTION_FAIL:
2062 p_result->rcode = RLM_MODULE_FAIL;
2063 goto finish;
2064
2066 autz_ctx->status = LDAP_AUTZ_POST_PROFILES;
2068
2069 default:
2070 break;
2071 }
2072 }
2074
2076 if (autz_ctx->profiles_applied > 0) autz_ctx->rcode = RLM_MODULE_UPDATED;
2077 break;
2078 }
2079
2080 p_result->rcode = autz_ctx->rcode;
2081
2082finish:
2083 return ret;
2084}
2085
2086/** Clear up when cancelling a mod_authorize call
2087 *
2088 */
2089static void mod_authorize_cancel(module_ctx_t const *mctx, UNUSED request_t *request, UNUSED fr_signal_t action)
2090{
2091 ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(mctx->rctx, ldap_autz_ctx_t);
2092
2093 if (autz_ctx->query && autz_ctx->query->treq) trunk_request_signal_cancel(autz_ctx->query->treq);
2094}
2095
2096/** Ensure authorization context is properly cleared up
2097 *
2098 */
2099static int autz_ctx_free(ldap_autz_ctx_t *autz_ctx)
2100{
2101 talloc_free(autz_ctx->expanded.ctx);
2102 return 0;
2103}
2104
2105static unlang_action_t CC_HINT(nonnull) mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
2106{
2108 fr_ldap_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, fr_ldap_thread_t);
2109 ldap_autz_ctx_t *autz_ctx;
2110 fr_ldap_map_exp_t *expanded;
2111 ldap_autz_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_autz_call_env_t);
2112
2113 MEM(autz_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_autz_ctx_t));
2114 talloc_set_destructor(autz_ctx, autz_ctx_free);
2115 expanded = &autz_ctx->expanded;
2116
2117 /*
2118 * Don't be tempted to add a check for User-Name or
2119 * User-Password here. LDAP authorization can be used
2120 * for many things besides searching for users.
2121 */
2122 if (fr_ldap_map_expand(autz_ctx, expanded, request, call_env->user_map, inst->valuepair_attr,
2123 inst->profile.check_attr, inst->profile.fallthrough_attr) < 0) {
2124 fail:
2125 talloc_free(autz_ctx);
2127 }
2128
2129 autz_ctx->ttrunk = fr_thread_ldap_trunk_get(thread, inst->handle_config.server, inst->handle_config.admin_identity,
2130 inst->handle_config.admin_password, request, &inst->handle_config);
2131 if (!autz_ctx->ttrunk) goto fail;
2132
2133#define CHECK_EXPANDED_SPACE(_expanded) fr_assert((size_t)_expanded->count < (NUM_ELEMENTS(_expanded->attrs) - 1));
2134
2135 /*
2136 * Add any additional attributes we need for checking access, memberships, and profiles
2137 */
2138 if (inst->user.obj_access_attr) {
2139 CHECK_EXPANDED_SPACE(expanded);
2140 expanded->attrs[expanded->count++] = inst->user.obj_access_attr;
2141 }
2142
2143 if (inst->group.userobj_membership_attr &&
2144 (inst->group.cacheable_dn || inst->group.cacheable_name ||
2145 inst->group.profile_attr || inst->group.profile_attr_suspend)) {
2146 CHECK_EXPANDED_SPACE(expanded);
2147 expanded->attrs[expanded->count++] = inst->group.userobj_membership_attr;
2148 }
2149
2150 if (inst->user.profile_attr) {
2151 CHECK_EXPANDED_SPACE(expanded);
2152 expanded->attrs[expanded->count++] = inst->user.profile_attr;
2153 }
2154
2155 if (inst->user.profile_attr_suspend) {
2156 CHECK_EXPANDED_SPACE(expanded);
2157 expanded->attrs[expanded->count++] = inst->user.profile_attr_suspend;
2158 }
2159 expanded->attrs[expanded->count] = NULL;
2160
2161 autz_ctx->dlinst = mctx->mi;
2162 autz_ctx->inst = inst;
2163 autz_ctx->call_env = call_env;
2164 autz_ctx->status = LDAP_AUTZ_FIND;
2165 autz_ctx->rcode = RLM_MODULE_OK;
2166
2167 if (unlikely(unlang_module_yield(request,
2170 autz_ctx) == UNLANG_ACTION_FAIL)) {
2171 talloc_free(autz_ctx);
2173 }
2174
2175 return rlm_ldap_find_user_async(autz_ctx, p_result,
2176 autz_ctx->inst, request, &autz_ctx->call_env->user_base,
2177 &autz_ctx->call_env->user_filter, autz_ctx->ttrunk, autz_ctx->expanded.attrs,
2178 &autz_ctx->query);
2179}
2180
2181/** Cancel an in progress user modification.
2182 *
2183 */
2184static void user_modify_cancel(module_ctx_t const *mctx, UNUSED request_t *request, UNUSED fr_signal_t action)
2185{
2186 ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(mctx->rctx, ldap_user_modify_ctx_t);
2187
2188 if (!usermod_ctx->query || !usermod_ctx->query->treq) return;
2189
2190 trunk_request_signal_cancel(usermod_ctx->query->treq);
2191}
2192
2193/** Handle results of user modification.
2194 *
2195 */
2196static unlang_action_t CC_HINT(nonnull) user_modify_final(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
2197{
2198 ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(mctx->rctx, ldap_user_modify_ctx_t);
2199 fr_ldap_query_t *query = usermod_ctx->query;
2200 rlm_rcode_t rcode = RLM_MODULE_OK;
2201
2202 switch (query->ret) {
2204 break;
2205
2207 case LDAP_RESULT_BAD_DN:
2208 RDEBUG2("User object \"%s\" not modified", usermod_ctx->dn);
2209 rcode = RLM_MODULE_INVALID;
2210 break;
2211
2213 rcode = RLM_MODULE_TIMEOUT;
2214 break;
2215
2216 default:
2217 rcode = RLM_MODULE_FAIL;
2218 break;
2219 }
2220
2221 talloc_free(usermod_ctx);
2222 RETURN_UNLANG_RCODE(rcode);
2223}
2224
2226{
2227 ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(mctx->rctx, ldap_user_modify_ctx_t);
2228 ldap_usermod_call_env_t *call_env = usermod_ctx->call_env;
2229 LDAPMod **modify;
2230 ldap_mod_tmpl_t *mod;
2231 fr_value_box_t *vb = NULL;
2232 int mod_no = usermod_ctx->expanded_mods, i = 0;
2233 struct berval **value_refs;
2234 struct berval *values;
2235
2236 mod = call_env->mod[usermod_ctx->current_mod];
2237
2238 /*
2239 * If the tmpl produced no boxes, skip
2240 */
2241 if ((mod->op != T_OP_CMP_FALSE) && (fr_value_box_list_num_elements(&usermod_ctx->expanded) == 0)) {
2242 RDEBUG2("Expansion \"%s\" produced no value, skipping attribute \"%s\"", mod->tmpl->name, mod->attr);
2243 goto next;
2244 }
2245
2246 switch (mod->op) {
2247 /*
2248 * T_OP_EQ is *NOT* supported, it is impossible to
2249 * support because of the lack of transactions in LDAP
2250 *
2251 * To allow for binary data, all data is provided as berval which
2252 * requires the operation to be logical ORed with LDAP_MOD_BVALUES
2253 */
2254 case T_OP_ADD_EQ:
2255 usermod_ctx->mod_s[mod_no].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
2256 break;
2257
2258 case T_OP_SET:
2259 usermod_ctx->mod_s[mod_no].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
2260 break;
2261
2262 case T_OP_SUB_EQ:
2263 case T_OP_CMP_FALSE:
2264 usermod_ctx->mod_s[mod_no].mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
2265 break;
2266
2267 case T_OP_INCRM:
2268 usermod_ctx->mod_s[mod_no].mod_op = LDAP_MOD_INCREMENT | LDAP_MOD_BVALUES;
2269 break;
2270
2271 default:
2272 REDEBUG("Operator '%s' is not supported for LDAP modify operations",
2273 fr_table_str_by_value(fr_tokens_table, mod->op, "<INVALID>"));
2274
2276 }
2277
2278 if (mod->op == T_OP_CMP_FALSE) {
2279 MEM(value_refs = talloc_zero_array(usermod_ctx, struct berval *, 1));
2280 } else {
2281 MEM(value_refs = talloc_zero_array(usermod_ctx, struct berval *,
2282 fr_value_box_list_num_elements(&usermod_ctx->expanded) + 1));
2283 MEM(values = talloc_zero_array(usermod_ctx, struct berval,
2284 fr_value_box_list_num_elements(&usermod_ctx->expanded)));
2285 while ((vb = fr_value_box_list_pop_head(&usermod_ctx->expanded))) {
2286 switch (vb->type) {
2287 case FR_TYPE_OCTETS:
2288 if (vb->vb_length == 0) continue;
2289 memcpy(&values[i].bv_val, &vb->vb_octets, sizeof(values[i].bv_val));
2290 values[i].bv_len = vb->vb_length;
2291 break;
2292
2293 case FR_TYPE_STRING:
2294 populate_string:
2295 if (vb->vb_length == 0) continue;
2296 memcpy(&values[i].bv_val, &vb->vb_strvalue, sizeof(values[i].bv_val));
2297 values[i].bv_len = vb->vb_length;
2298 break;
2299
2300 case FR_TYPE_GROUP:
2301 {
2302 fr_value_box_t *vb_head = fr_value_box_list_head(&vb->vb_group);
2303 if (fr_value_box_list_concat_in_place(vb_head, vb_head, &vb->vb_group, FR_TYPE_STRING,
2304 FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
2305 RPEDEBUG("Failed concatenating update value");
2307 }
2308 vb = vb_head;
2309 goto populate_string;
2310 }
2311
2312 case FR_TYPE_FIXED_SIZE:
2313 if (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0) {
2314 RPEDEBUG("Failed casting update value");
2316 }
2317 goto populate_string;
2318
2319 default:
2320 fr_assert(0);
2321
2322 }
2323 value_refs[i] = &values[i];
2324 i++;
2325 }
2326 if (i == 0) {
2327 RDEBUG2("Expansion \"%s\" produced zero length value, skipping attribute \"%s\"", mod->tmpl->name, mod->attr);
2328 goto next;
2329 }
2330 }
2331
2332 /*
2333 * Now everything is evaluated, set up the pointers for the LDAPMod
2334 */
2335 memcpy(&(usermod_ctx->mod_s[mod_no].mod_type), &mod->attr, sizeof(usermod_ctx->mod_s[mod_no].mod_type));
2336 usermod_ctx->mod_s[mod_no].mod_bvalues = value_refs;
2337 usermod_ctx->mod_p[mod_no] = &usermod_ctx->mod_s[mod_no];
2338
2339 usermod_ctx->expanded_mods++;
2340 usermod_ctx->mod_p[usermod_ctx->expanded_mods] = NULL;
2341
2342next:
2343 usermod_ctx->current_mod++;
2344
2345 /*
2346 * Keep calling until we've completed all the modifications
2347 */
2348 if (usermod_ctx->current_mod < usermod_ctx->num_mods) {
2350 if (unlang_tmpl_push(usermod_ctx, NULL, &usermod_ctx->expanded, request,
2351 usermod_ctx->call_env->mod[usermod_ctx->current_mod]->tmpl, NULL, UNLANG_SUB_FRAME) < 0) RETURN_UNLANG_FAIL;
2353 }
2354
2355 modify = usermod_ctx->mod_p;
2356
2358
2359 return fr_ldap_trunk_modify(usermod_ctx, &usermod_ctx->query, request, usermod_ctx->ttrunk,
2360 usermod_ctx->dn, modify, NULL, NULL);
2361}
2362
2363/** Take the retrieved user DN and launch the async tmpl expansion of mod_values.
2364 *
2365 */
2366static unlang_action_t CC_HINT(nonnull) user_modify_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
2367{
2368 ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(mctx->rctx, ldap_user_modify_ctx_t);
2369
2370 /*
2371 * If an LDAP search was used to find the user DN
2372 * usermod_ctx->dn will be NULL.
2373 */
2374 if (!usermod_ctx->dn) usermod_ctx->dn = rlm_find_user_dn_cached(mctx->mi->data, request);
2375
2376 if (!usermod_ctx->dn) {
2377 fail:
2378 talloc_free(usermod_ctx);
2380 }
2381
2382 /*
2383 * Allocate arrays to hold mods. mod_p is one element longer to hold a terminating NULL entry
2384 */
2385 MEM(usermod_ctx->mod_p = talloc_zero_array(usermod_ctx, LDAPMod *, usermod_ctx->num_mods + 1));
2386 MEM(usermod_ctx->mod_s = talloc_array(usermod_ctx, LDAPMod, usermod_ctx->num_mods));
2387 fr_value_box_list_init(&usermod_ctx->expanded);
2388
2389 if (unlang_module_yield(request, user_modify_mod_build_resume, NULL, 0, usermod_ctx) == UNLANG_ACTION_FAIL) goto fail;
2390;
2391 if (unlang_tmpl_push(usermod_ctx, NULL, &usermod_ctx->expanded, request,
2392 usermod_ctx->call_env->mod[0]->tmpl, NULL, UNLANG_SUB_FRAME) < 0) goto fail;
2393
2395}
2396
2397/** Modify user's object in LDAP
2398 *
2399 * Process a modification map to update a user object in the LDAP directory.
2400 *
2401 * The module method called in "accouting" and "send" sections.
2402 */
2403static unlang_action_t CC_HINT(nonnull) mod_modify(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
2404{
2406 ldap_usermod_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_call_env_t);
2407 fr_ldap_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, fr_ldap_thread_t);
2408 ldap_user_modify_ctx_t *usermod_ctx = NULL;
2409
2410 size_t num_mods = talloc_array_length(call_env->mod);
2411
2412 if (num_mods == 0) RETURN_UNLANG_NOOP;
2413
2414 /*
2415 * Include a talloc pool allowing for one value per modification
2416 */
2418 2 * num_mods + 2,
2419 (sizeof(struct berval) + (sizeof(struct berval *) * 2) +
2420 (sizeof(LDAPMod) + sizeof(LDAPMod *))) * num_mods));
2421 *usermod_ctx = (ldap_user_modify_ctx_t) {
2422 .inst = inst,
2423 .call_env = call_env,
2424 .num_mods = num_mods
2425 };
2426
2427 usermod_ctx->ttrunk = fr_thread_ldap_trunk_get(thread, inst->handle_config.server,
2428 inst->handle_config.admin_identity,
2429 inst->handle_config.admin_password,
2430 request, &inst->handle_config);
2431 if (!usermod_ctx->ttrunk) {
2432 REDEBUG("Unable to get LDAP trunk for update");
2433 talloc_free(usermod_ctx);
2435 }
2436
2437 usermod_ctx->dn = rlm_find_user_dn_cached(inst, request);
2438 /*
2439 * Find the user first
2440 */
2441 if (!usermod_ctx->dn) {
2442 if (unlang_module_yield(request, user_modify_resume, NULL, 0, usermod_ctx) == UNLANG_ACTION_FAIL) {
2443 talloc_free(usermod_ctx);
2445 }
2446
2447 /* Pushes a frame for user resolution */
2448 if (rlm_ldap_find_user_async(usermod_ctx,
2449 p_result,
2450 usermod_ctx->inst, request,
2451 &usermod_ctx->call_env->user_base,
2452 &usermod_ctx->call_env->user_filter,
2453 usermod_ctx->ttrunk, NULL, NULL) == UNLANG_ACTION_FAIL) {
2455 }
2456
2458 }
2459
2460 {
2461 module_ctx_t our_mctx = *mctx;
2462 our_mctx.rctx = usermod_ctx;
2463
2464 return user_modify_resume(p_result, &our_mctx, request);
2465 }
2466}
2467
2468/** Detach from the LDAP server and cleanup internal state.
2469 *
2470 */
2471static int mod_detach(module_detach_ctx_t const *mctx)
2472{
2473 rlm_ldap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_ldap_t);
2474
2475 if (inst->user.obj_sort_ctrl) ldap_control_free(inst->user.obj_sort_ctrl);
2476 if (inst->profile.obj_sort_ctrl) ldap_control_free(inst->profile.obj_sort_ctrl);
2477
2478 return 0;
2479}
2480
2481static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
2482 CONF_ITEM *ci,
2483 UNUSED call_env_ctx_t const *cec, call_env_parser_t const *rule)
2484{
2485 map_list_t *maps;
2486 CONF_SECTION *update = cf_item_to_section(ci);
2487 ldap_update_rules_t const *ur = rule->uctx;
2488
2489 bool expect_password = false;
2490
2491 /*
2492 * Build the attribute map
2493 */
2494 {
2495 map_t const *map = NULL;
2496 tmpl_attr_t const *ar;
2497 call_env_parsed_t *parsed;
2498
2499 MEM(parsed = call_env_parsed_add(ctx, out,
2501 .name = "update",
2502 .flags = CALL_ENV_FLAG_PARSE_ONLY,
2503 .pair = {
2504 .parsed = {
2505 .offset = ur->map_offset,
2507 }
2508 }
2509 }));
2510
2511 MEM(maps = talloc(parsed, map_list_t));
2512 map_list_init(maps);
2513
2514 if (update && (map_afrom_cs(maps, maps, update, t_rules, t_rules, fr_ldap_map_verify,
2515 NULL, LDAP_MAX_ATTRMAP)) < 0) {
2516 call_env_parsed_free(out, parsed);
2517 return -1;
2518 }
2519 /*
2520 * Check map to see if a password is being retrieved.
2521 * fr_ldap_map_verify ensures that all maps have attributes on the LHS.
2522 * All passwords have a common parent attribute of attr_password
2523 */
2524 while ((map = map_list_next(maps, map))) {
2525 ar = tmpl_attr_tail(map->lhs);
2526 if (ar->da->parent == attr_password) {
2527 expect_password = true;
2528 break;
2529 }
2530 }
2531 call_env_parsed_set_data(parsed, maps);
2532 }
2533
2534 /*
2535 * Write out whether we expect a password to be returned from the ldap data
2536 */
2537 if (ur->expect_password_offset >= 0) {
2538 call_env_parsed_t *parsed;
2539 fr_value_box_t *vb;
2540
2541 MEM(parsed = call_env_parsed_add(ctx, out,
2543 .name = "expect_password",
2544 .flags = CALL_ENV_FLAG_PARSE_ONLY,
2545 .pair = {
2546 .parsed = {
2547 .offset = ur->expect_password_offset,
2549 }
2550 }
2551 }));
2552 MEM(vb = fr_value_box_alloc(parsed, FR_TYPE_BOOL, NULL));
2553 vb->vb_bool = expect_password;
2554 call_env_parsed_set_value(parsed, vb);
2555 }
2556
2557 return 0;
2558}
2559
2560static int ldap_mod_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
2561 CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
2562{
2563 CONF_SECTION const *subcs = NULL;
2564 CONF_PAIR const *to_parse = NULL;
2565 tmpl_t *parsed_tmpl;
2566 call_env_parsed_t *parsed_env;
2567 char *section2, *p;
2568 ssize_t count, slen, multi_index = 0;
2569 ldap_mod_tmpl_t *mod;
2570
2572
2573 section2 = talloc_strdup(NULL, section_name_str(cec->asked->name2));
2574 p = section2;
2575 while (*p != '\0') {
2576 *(p) = tolower((uint8_t)*p);
2577 p++;
2578 }
2579
2580 if (!ci) {
2581 not_found:
2582 cf_log_warn(ci, "No section found for \"%s.%s\" in module \"%s\", this call will have no effect.",
2583 section_name_str(cec->asked->name1), section2, cec->mi->name);
2584 free:
2585 talloc_free(section2);
2586 return 0;
2587 }
2588
2589 subcs = cf_section_find(cf_item_to_section(ci), section2, CF_IDENT_ANY);
2590 if (!subcs) goto not_found;
2591
2592 subcs = cf_section_find(subcs, "update", CF_IDENT_ANY);
2593 if (!subcs) {
2594 cf_log_warn(ci, "No update found inside \"%s -> %s\" in module \"%s\"",
2595 section_name_str(cec->asked->name1), section2, cec->mi->name);
2596 goto free;
2597 }
2598
2600 if (count == 0) {
2601 cf_log_warn(ci, "No modifications found for \"%s.%s\" in module \"%s\"",
2602 section_name_str(cec->asked->name1), section2, cec->mi->name);
2603 goto free;
2604 }
2605 talloc_free(section2);
2606
2607 while ((to_parse = cf_pair_next(subcs, to_parse))) {
2608 switch (cf_pair_operator(to_parse)) {
2609 case T_OP_SET:
2610 case T_OP_ADD_EQ:
2611 case T_OP_SUB_EQ:
2612 case T_OP_CMP_FALSE:
2613 case T_OP_INCRM:
2614 break;
2615
2616 default:
2617 cf_log_perr(to_parse, "Invalid operator for LDAP modification");
2618 return -1;
2619 }
2620
2621 MEM(parsed_env = call_env_parsed_add(ctx, out,
2626 }));
2627
2628 slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
2629 &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_strlen(cf_pair_value(to_parse))),
2631 t_rules);
2632
2633 if (slen <= 0) {
2634 cf_canonicalize_error(to_parse, slen, "Failed parsing LDAP modification \"%s\"", cf_pair_value(to_parse));
2635 error:
2636 call_env_parsed_free(out, parsed_env);
2637 return -1;
2638 }
2639 if (tmpl_needs_resolving(parsed_tmpl) &&
2640 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = t_rules->attr.dict_def }) <0)) {
2641 cf_log_perr(to_parse, "Failed resolving LDAP modification \"%s\"", cf_pair_value(to_parse));
2642 goto error;
2643 }
2644
2645 MEM(mod = talloc(parsed_env, ldap_mod_tmpl_t));
2646 mod->attr = cf_pair_attr(to_parse);
2647 mod->tmpl = parsed_tmpl;
2648 mod->op = cf_pair_operator(to_parse);
2649
2650 call_env_parsed_set_multi_index(parsed_env, count, multi_index++);
2651 call_env_parsed_set_data(parsed_env, mod);
2652 }
2653
2654 return 0;
2655}
2656
2657static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci,
2658 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
2659{
2661 char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
2662 tmpl_t *parsed;
2663
2664 if (fr_ldap_filter_to_tmpl(ctx, t_rules, filters, NUM_ELEMENTS(filters), &parsed) < 0) return -1;
2665
2666 *(void **)out = parsed;
2667 return 0;
2668}
2669
2670/** Clean up thread specific data structure
2671 *
2672 */
2674{
2675 fr_ldap_thread_t *t = talloc_get_type_abort(mctx->thread, fr_ldap_thread_t);
2676 void **trunks_to_free;
2677 int i;
2678
2679 if (fr_rb_flatten_inorder(NULL, &trunks_to_free, t->trunks) < 0) return -1;
2680
2681 for (i = talloc_array_length(trunks_to_free) - 1; i >= 0; i--) talloc_free(trunks_to_free[i]);
2682 talloc_free(trunks_to_free);
2683 talloc_free(t->trunks);
2684
2685 return 0;
2686}
2687
2688/** Initialise thread specific data structure
2689 *
2690 */
2692{
2693 rlm_ldap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_ldap_t);
2694 fr_ldap_thread_t *t = talloc_get_type_abort(mctx->thread, fr_ldap_thread_t);
2695 fr_ldap_thread_trunk_t *ttrunk;
2696
2697 /*
2698 * Initialise tree for connection trunks used by this thread
2699 */
2701
2702 t->config = &inst->handle_config;
2703 t->trunk_conf = &inst->trunk_conf;
2704 t->bind_trunk_conf = &inst->bind_trunk_conf;
2705 t->el = mctx->el;
2706 t->trigger_args = inst->trigger_args;
2707 t->bind_trigger_args = inst->bind_trigger_args;
2708
2709 /*
2710 * Launch trunk for module default connection
2711 */
2712 ttrunk = fr_thread_ldap_trunk_get(t, inst->handle_config.server, inst->handle_config.admin_identity,
2713 inst->handle_config.admin_password, NULL, &inst->handle_config);
2714 if (!ttrunk) {
2715 ERROR("Unable to launch LDAP trunk");
2716 return -1;
2717 }
2718
2719 /*
2720 * Set up a per-thread LDAP trunk to use for bind auths
2721 */
2723
2725
2726 return 0;
2727}
2728
2729/** Instantiate the module
2730 *
2731 * Creates a new instance of the module reading parameters from a configuration section.
2732 *
2733 * @param [in] mctx configuration data.
2734 * @return
2735 * - 0 on success.
2736 * - < 0 on failure.
2737 */
2738static int mod_instantiate(module_inst_ctx_t const *mctx)
2739{
2740 size_t i;
2741
2742 CONF_SECTION *options;
2743 rlm_ldap_boot_t const *boot = talloc_get_type_abort(mctx->mi->boot, rlm_ldap_boot_t);
2744 rlm_ldap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_ldap_t);
2745 CONF_SECTION *conf = mctx->mi->conf;
2746
2747 inst->mi = mctx->mi; /* Cached for IO callbacks */
2748 inst->group.da = boot->group_da;
2749 inst->group.cache_da = boot->cache_da;
2750 inst->user.da = boot->user_da;
2751
2752 inst->handle_config.name = talloc_typed_asprintf(inst, "rlm_ldap (%s)", mctx->mi->name);
2753
2754 /*
2755 * Trunks used for bind auth can only have one request in flight per connection.
2756 */
2757 inst->bind_trunk_conf.target_req_per_conn = 1;
2758 inst->bind_trunk_conf.max_req_per_conn = 1;
2759
2760 /*
2761 * Set sizes for trunk request pool.
2762 */
2763 inst->bind_trunk_conf.req_pool_headers = 2;
2764 inst->bind_trunk_conf.req_pool_size = sizeof(fr_ldap_bind_auth_ctx_t) + sizeof(fr_ldap_sasl_ctx_t);
2765
2766 options = cf_section_find(conf, "options", NULL);
2767 if (!options || !cf_pair_find(options, "chase_referrals")) {
2768 inst->handle_config.chase_referrals_unset = true; /* use OpenLDAP defaults */
2769 }
2770
2771 /*
2772 * Sanity checks for cacheable groups code.
2773 */
2774 if (inst->group.cacheable_name && inst->group.obj_membership_filter) {
2775 if (!inst->group.obj_name_attr) {
2776 cf_log_err(conf, "Configuration item 'group.name_attribute' must be set if cacheable "
2777 "group names are enabled");
2778
2779 return -1;
2780 }
2781 }
2782
2783 /*
2784 * If we have a *pair* as opposed to a *section*
2785 * then the module is referencing another ldap module's
2786 * connection pool.
2787 */
2788 if (!cf_pair_find(conf, "pool")) {
2789 if (!inst->handle_config.server_str) {
2790 cf_log_err(conf, "Configuration item 'server' must have a value");
2791 return -1;
2792 }
2793 }
2794
2795#ifndef WITH_SASL
2796 if (inst->handle_config.admin_sasl.mech) {
2797 cf_log_err(conf, "Configuration item 'sasl.mech' not supported. "
2798 "Linked libldap does not provide ldap_sasl_interactive_bind function");
2799 return -1;
2800 }
2801#endif
2802
2803 /*
2804 * Initialise server with zero length string to
2805 * make code below simpler.
2806 */
2807 inst->handle_config.server = talloc_strdup(inst, "");
2808
2809 /*
2810 * Now iterate over all the 'server' config items
2811 */
2812 for (i = 0; i < talloc_array_length(inst->handle_config.server_str); i++) {
2813 char const *value = inst->handle_config.server_str[i];
2814 size_t j;
2815
2816 /*
2817 * Explicitly prevent multiple server definitions
2818 * being used in the same string.
2819 */
2820 for (j = 0; j < talloc_strlen(value); j++) {
2821 switch (value[j]) {
2822 case ' ':
2823 case ',':
2824 case ';':
2825 cf_log_err(conf, "Invalid character '%c' found in 'server' configuration item",
2826 value[j]);
2827 return -1;
2828
2829 default:
2830 continue;
2831 }
2832 }
2833
2834 /*
2835 * Split original server value out into URI, server and port
2836 * so whatever initialization function we use later will have
2837 * the server information in the format it needs.
2838 */
2839 if (ldap_is_ldap_url(value)) {
2840 if (fr_ldap_server_url_check(&inst->handle_config, value, conf) < 0) return -1;
2841 } else
2842 /*
2843 * If it's not an URL, then just treat server as a hostname.
2844 */
2845 {
2846 if (fr_ldap_server_config_check(&inst->handle_config, value, conf) < 0) return -1;
2847 }
2848 }
2849
2850 /*
2851 * inst->handle_config.server be unset if connection pool sharing is used.
2852 */
2853 if (inst->handle_config.server) {
2854 inst->handle_config.server[talloc_array_length(inst->handle_config.server) - 2] = '\0';
2855 DEBUG4("rlm_ldap (%s) - LDAP server string: %s", mctx->mi->name, inst->handle_config.server);
2856 }
2857
2858 /*
2859 * Workaround for servers which support LDAPS but not START TLS
2860 */
2861 if (inst->handle_config.port == LDAPS_PORT || inst->handle_config.tls_mode) {
2862 inst->handle_config.tls_mode = LDAP_OPT_X_TLS_HARD;
2863 } else {
2864 inst->handle_config.tls_mode = 0;
2865 }
2866
2867 /*
2868 * Convert dereference strings to enumerated constants
2869 */
2870 if (inst->handle_config.dereference_str) {
2871 inst->handle_config.dereference = fr_table_value_by_str(fr_ldap_dereference,
2872 inst->handle_config.dereference_str, -1);
2873 if (inst->handle_config.dereference < 0) {
2874 cf_log_err(conf, "Invalid 'dereference' value \"%s\", expected 'never', 'searching', "
2875 "'finding' or 'always'", inst->handle_config.dereference_str);
2876 return -1;
2877 }
2878 }
2879
2880 /*
2881 * Build the server side sort control for user / profile objects
2882 */
2883#define SSS_CONTROL_BUILD(_obj) if (inst->_obj.obj_sort_by) { \
2884 LDAPSortKey **keys; \
2885 int ret; \
2886 ret = ldap_create_sort_keylist(&keys, UNCONST(char *, inst->_obj.obj_sort_by)); \
2887 if (ret != LDAP_SUCCESS) { \
2888 cf_log_err(conf, "Invalid " STRINGIFY(_obj) ".sort_by value \"%s\": %s", \
2889 inst->_obj.obj_sort_by, ldap_err2string(ret)); \
2890 return -1; \
2891 } \
2892 /* \
2893 * Always set the control as critical, if it's not needed \
2894 * the user can comment it out... \
2895 */ \
2896 ret = ldap_create_sort_control(ldap_global_handle, keys, 1, &inst->_obj.obj_sort_ctrl); \
2897 ldap_free_sort_keylist(keys); \
2898 if (ret != LDAP_SUCCESS) { \
2899 ERROR("Failed creating server sort control: %s", ldap_err2string(ret)); \
2900 return -1; \
2901 } \
2902 }
2903
2904 SSS_CONTROL_BUILD(user)
2905 SSS_CONTROL_BUILD(profile)
2906
2907 /*
2908 * Bulk retrieval matches profile objects by their exact DN,
2909 * so subtree (non-base scope) semantics cannot be preserved.
2910 */
2911 switch (inst->profile.search_mode) {
2913 if (inst->profile.obj_sort_ctrl && (inst->profile.obj_scope == LDAP_SCOPE_BASE)) {
2914 inst->profile.search_mode = LDAP_PROFILE_SEARCH_MODE_BULK;
2915 } else {
2916 inst->profile.search_mode = LDAP_PROFILE_SEARCH_MODE_SEQ;
2917 }
2918 break;
2919
2921 if (inst->profile.obj_scope != LDAP_SCOPE_BASE) {
2922 cf_log_err(conf, "'profile.search_mode = bulk' requires 'profile.scope = base'");
2923 return -1;
2924 }
2925 if (!inst->profile.obj_sort_ctrl) {
2926 cf_log_err(conf, "'profile.search_mode = bulk' requires 'profile.sort_by', "
2927 "else profile evaluation order is non-deterministic");
2928 return -1;
2929 }
2930 break;
2931
2933 break;
2934 }
2935
2936 if (inst->handle_config.tls_require_cert_str) {
2937 /*
2938 * Convert cert strictness to enumerated constants
2939 */
2940 inst->handle_config.tls_require_cert = fr_table_value_by_str(fr_ldap_tls_require_cert,
2941 inst->handle_config.tls_require_cert_str, -1);
2942 if (inst->handle_config.tls_require_cert < 0) {
2943 cf_log_err(conf, "Invalid 'tls.require_cert' value \"%s\", expected 'never', "
2944 "'demand', 'allow', 'try' or 'hard'", inst->handle_config.tls_require_cert_str);
2945 return -1;
2946 }
2947 }
2948
2949 if (inst->handle_config.tls_min_version_str) {
2950#ifdef LDAP_OPT_X_TLS_PROTOCOL_TLS1_3
2951 if (strcmp(inst->handle_config.tls_min_version_str, "1.3") == 0) {
2952 inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_3;
2953
2954 } else
2955#endif
2956 if (strcmp(inst->handle_config.tls_min_version_str, "1.2") == 0) {
2957 inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_2;
2958
2959 } else if (strcmp(inst->handle_config.tls_min_version_str, "1.1") == 0) {
2960 inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_1;
2961
2962 } else if (strcmp(inst->handle_config.tls_min_version_str, "1.0") == 0) {
2963 inst->handle_config.tls_min_version = LDAP_OPT_X_TLS_PROTOCOL_TLS1_0;
2964
2965 } else {
2966 cf_log_err(conf, "Invalid 'tls.tls_min_version' value \"%s\"", inst->handle_config.tls_min_version_str);
2967 return -1;
2968 }
2969 }
2970
2971 if (inst->trunk_conf.conn_triggers) {
2972 MEM(inst->trigger_args = fr_pair_list_alloc(inst));
2973 if (module_trigger_args_build(inst->trigger_args, inst->trigger_args, cf_section_find(conf, "pool", NULL),
2975 .module = mctx->mi->module->name,
2976 .name = mctx->mi->name,
2977 .server = inst->handle_config.server,
2978 .port = inst->handle_config.port
2979 }) < 0) return -1;
2980 }
2981
2982 if (inst->bind_trunk_conf.conn_triggers) {
2983 MEM(inst->bind_trigger_args = fr_pair_list_alloc(inst));
2984 if (module_trigger_args_build(inst->bind_trigger_args, inst->bind_trigger_args, cf_section_find(conf, "bind_pool", NULL),
2986 .module = mctx->mi->module->name,
2987 .name = mctx->mi->name,
2988 .server = inst->handle_config.server,
2989 .port = inst->handle_config.port
2990 }) < 0) return -1;
2991 }
2992 return 0;
2993}
2994
2995/** Bootstrap the module
2996 *
2997 * Define attributes.
2998 *
2999 * @param[in] mctx configuration data.
3000 * @return
3001 * - 0 on success.
3002 * - < 0 on failure.
3003 */
3004static int mod_bootstrap(module_inst_ctx_t const *mctx)
3005{
3006 rlm_ldap_boot_t *boot = talloc_get_type_abort(mctx->mi->boot, rlm_ldap_boot_t);
3007 rlm_ldap_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_ldap_t);
3008 CONF_SECTION *conf = mctx->mi->conf;
3009 char buffer[256];
3010 char const *group_attribute;
3011 xlat_t *xlat;
3012
3013 if (inst->group.attribute) {
3014 group_attribute = inst->group.attribute;
3015 } else if (cf_section_name2(conf)) {
3016 snprintf(buffer, sizeof(buffer), "%s-LDAP-Group", mctx->mi->name);
3017 group_attribute = buffer;
3018 } else {
3019 group_attribute = "LDAP-Group";
3020 }
3021
3022 boot->group_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), group_attribute);
3023
3024 /*
3025 * If the group attribute was not in the dictionary, create it
3026 */
3027 if (!boot->group_da) {
3029 group_attribute, FR_TYPE_STRING, NULL) < 0) {
3030 PERROR("Error creating group attribute");
3031 return -1;
3032
3033 }
3034 boot->group_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), group_attribute);
3035 }
3036
3037 /*
3038 * Setup the cache attribute
3039 */
3040 if (inst->group.cache_attr_str) {
3041 boot->cache_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), inst->group.cache_attr_str);
3042 if (!boot->cache_da) {
3044 inst->group.cache_attr_str, FR_TYPE_STRING, NULL) < 0) {
3045 PERROR("Error creating cache attribute");
3046 return -1;
3047 }
3048 boot->cache_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), inst->group.cache_attr_str);
3049 }
3050 } else {
3051 boot->cache_da = boot->group_da; /* Default to the group_da */
3052 }
3053
3054
3055 if (inst->user.dn_attr_str) {
3056 boot->user_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), inst->user.dn_attr_str);
3057 if (!boot->user_da) {
3059 inst->user.dn_attr_str, FR_TYPE_STRING, NULL) < 0) {
3060 PERROR("Error creating user DN cache attribute");
3061 return -1;
3062 }
3063 boot->user_da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), inst->user.dn_attr_str);
3064 }
3065 }
3066
3067 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, ldap_xlat, FR_TYPE_STRING);
3069
3070 if (unlikely(!(xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "group", ldap_group_xlat,
3071 FR_TYPE_BOOL)))) return -1;
3074
3075 if (unlikely(!(xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "profile", ldap_profile_xlat,
3076 FR_TYPE_BOOL)))) return -1;
3079
3080 if (unlikely(!module_rlm_xlat_register(mctx->mi->boot, mctx, "whoami", ldap_whoami_xlat,
3081 FR_TYPE_STRING))) return -1;
3082
3084
3085 return 0;
3086}
3087
3088static int mod_load(void)
3089{
3090 xlat_t *xlat;
3091
3092 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.dn.escape", ldap_dn_escape_xlat, FR_TYPE_STRING)))) return -1;
3096
3097 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.dn.safe", xlat_transparent, FR_TYPE_STRING)))) return -1;
3101
3102 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.dn.unescape", ldap_uri_unescape_xlat, FR_TYPE_STRING)))) return -1;
3105
3106 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.filter.escape", ldap_filter_escape_xlat, FR_TYPE_STRING)))) return -1;
3110
3111 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.filter.safe", xlat_transparent, FR_TYPE_STRING)))) return -1;
3115
3116 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.filter.unescape", ldap_uri_unescape_xlat, FR_TYPE_STRING)))) return -1;
3119
3120 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.attr_option", ldap_xlat_uri_attr_option, FR_TYPE_STRING)))) return -1;
3123
3124 /*
3125 * ldap.uri.* are kept as aliases for ldap.dn.* so that existing configs
3126 * continue to work. They use the same safe_for token for now; if the URI
3127 * context ever needs its own rules, a separate token can be introduced.
3128 */
3129 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.escape", ldap_dn_escape_xlat, FR_TYPE_STRING)))) return -1;
3133
3134 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.safe", xlat_transparent, FR_TYPE_STRING)))) return -1;
3138
3139 if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.uri.unescape", ldap_uri_unescape_xlat, FR_TYPE_STRING)))) return -1;
3142
3143 return 0;
3144}
3145
3146static void mod_unload(void)
3147{
3148 xlat_func_unregister("ldap.dn.escape");
3149 xlat_func_unregister("ldap.dn.safe");
3150 xlat_func_unregister("ldap.dn.unescape");
3151 xlat_func_unregister("ldap.filter.escape");
3152 xlat_func_unregister("ldap.filter.safe");
3153 xlat_func_unregister("ldap.filter.unescape");
3154 xlat_func_unregister("ldap.uri.escape");
3155 xlat_func_unregister("ldap.uri.safe");
3156 xlat_func_unregister("ldap.uri.unescape");
3157}
3159/* globally exported name */
3160extern module_rlm_t rlm_ldap;
3162 .common = {
3163 .magic = MODULE_MAGIC_INIT,
3164 .name = "ldap",
3165 .flags = 0,
3168 .config = module_config,
3169 .onload = mod_load,
3170 .unload = mod_unload,
3171 .bootstrap = mod_bootstrap,
3172 .instantiate = mod_instantiate,
3173 .detach = mod_detach,
3175 .thread_instantiate = mod_thread_instantiate,
3176 .thread_detach = mod_thread_detach,
3177 },
3178 .method_group = {
3179 .bindings = (module_method_binding_t[]){
3180 /*
3181 * Hack to support old configurations
3182 */
3183 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_modify, .method_env = &accounting_usermod_method_env },
3184 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &authenticate_method_env },
3185 { .section = SECTION_NAME("authorize", CF_IDENT_ANY), .method = mod_authorize, .method_env = &authorize_method_env },
3186
3187 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_authorize, .method_env = &authorize_method_env },
3188 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_modify, .method_env = &send_usermod_method_env },
3190 }
3191 }
3192};
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:576
int n
Definition acutest.h:577
log_entry msg
Definition acutest.h:794
#define USES_APPLE_DEPRECATED_API
Definition build.h:499
#define RCSID(id)
Definition build.h:512
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:343
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
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:776
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:689
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:761
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:746
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:732
#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:1724
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:623
#define FR_CONF_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a matching CONF_PAIR is found
Definition cf_parse.h:409
#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:280
#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:334
#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:294
#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:268
#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:309
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:446
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition cf_parse.h:443
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:423
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
Common header for all CONF_* types.
Definition cf_priv.h:54
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
unsigned int cf_pair_count_descendents(CONF_SECTION const *cs)
Count the number of conf pairs beneath a section.
Definition cf_util.c:1660
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1359
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:1201
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
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:1594
fr_token_t cf_pair_operator(CONF_PAIR const *pair)
Return the operator of a pair.
Definition cf_util.c:1767
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1797
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:1568
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1753
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1737
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:345
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:423
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:352
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:346
#define CF_IDENT_ANY
Definition cf_util.h:80
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
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:1981
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4880
static fr_slen_t err
Definition dict.h:882
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:3505
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2639
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
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_filter_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 a string for use as an RFC 4515 filter assertion value.
Definition rlm_ldap.c:486
static xlat_action_t ldap_dn_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 a string for use in an RFC 4514 DN attribute value.
Definition rlm_ldap.c:446
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:897
static xlat_action_t ldap_whoami_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Return the authorization identity from the whoami response.
Definition rlm_ldap.c:1372
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:1104
static xlat_action_t ldap_whoami_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Perform the RFC 4532 whoami extended operation.
Definition rlm_ldap.c:1430
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:1235
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:640
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:531
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:748
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:950
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:438
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:1300
unlang_action_t rlm_ldap_group_profiles(unlang_result_t *p_result, request_t *request, ldap_autz_ctx_t *autz_ctx)
Search for profile DNs in the group objects the user is a member of.
Definition groups.c:852
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:1343
free(array)
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:2002
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2047
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2423
#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:139
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:1174
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:227
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:827
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
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:948
fr_ldap_control_t serverctrls[LDAP_MAX_CONTROLS]
Server controls specific to this query.
Definition base.h:453
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:377
fr_time_delta_t res_timeout
How long we wait for results.
Definition base.h:308
char const * admin_password
Password used in administrative bind.
Definition base.h:241
fr_ldap_config_t * config
Module instance config.
Definition base.h:384
int count
Index on next free element.
Definition base.h:376
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition util.c:275
size_t fr_ldap_filter_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Escape a string for use as an RFC 4515 filter assertion value.
Definition util.c:155
char * server
Initial server to bind to.
Definition base.h:234
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:727
LDAP * handle
libldap handle.
Definition base.h:343
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition base.h:239
fr_ldap_result_code_t ret
Result code.
Definition base.h:473
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:383
trunk_conf_t * trunk_conf
Module trunk config.
Definition base.h:385
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:459
int fr_ldap_map_getdn(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:210
size_t fr_ldap_dn_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg))
Escape a string for use as an RFC 4514 DN attribute value.
Definition util.c:69
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:940
int fr_ldap_result_values_len(size_t *num, size_t *strings_len, LDAP *handle, LDAPMessage *result, char const *attr)
Sum the lengths of an attribute's values across every entry of a result.
Definition util.c:653
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:1036
#define LDAP_MAX_ATTRMAP
Maximum number of mappings between LDAP and FreeRADIUS attributes.
Definition base.h:96
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:698
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:1133
talloc_str_list_t * fr_ldap_str_list_afrom_result(TALLOC_CTX *ctx, LDAP *handle, LDAPMessage *result, char const *attr, size_t extra)
Copy an attribute's values from every entry of a result into a string list.
Definition util.c:696
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:293
fr_pair_list_t * bind_trigger_args
Passed to trigger request for bind trunks.
Definition base.h:391
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:460
fr_ldap_result_code_t
LDAP query result codes.
Definition base.h:189
@ LDAP_RESULT_TIMEOUT
The query timed out.
Definition base.h:193
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:191
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:195
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:194
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:321
#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:386
#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:373
LDAPMessage * result
Head of LDAP results list.
Definition base.h:471
fr_event_list_t * el
Thread event list for callbacks / timeouts.
Definition base.h:387
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:373
fr_ldap_thread_trunk_t * bind_trunk
LDAP trunk used for bind auths.
Definition base.h:388
trunk_t * trunk
Connection trunk.
Definition base.h:408
fr_pair_list_t * trigger_args
Passed to trigger request for normal trunks.
Definition base.h:390
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:328
fr_timer_t * ev
Event for timing out the query.
Definition base.h:462
TALLOC_CTX * ctx
Context to allocate new attributes in.
Definition base.h:375
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition base.h:389
struct berval * fr_ldap_value_iter_alloc(int *err, fr_ldap_value_iter_t **out, TALLOC_CTX *ctx, LDAP *handle, LDAPMessage *entry, char const *attr)
Allocate a value iterator, released when the iterator is freed.
Definition util.c:623
LDAPURLDesc * ldap_url
parsed URL for current query if the source of the query was a URL.
Definition base.h:429
Holds arguments for async bind auth requests.
Definition base.h:616
Connection configuration.
Definition base.h:231
Tracks the state of a libldap connection handle.
Definition base.h:342
Result of expanding the RHS of a set of maps.
Definition base.h:371
LDAP query structure.
Definition base.h:425
Holds arguments for the async SASL bind operation.
Definition base.h:509
Thread specific structure to manage LDAP trunk connections.
Definition base.h:382
Thread LDAP trunk structure.
Definition base.h:402
State of an in place iteration over an attribute's values.
Definition base.h:968
#define FR_LDAP_COMMON_CONF(_conf)
Definition conf.h:19
size_t fr_ldap_scope_len
Definition base.c:75
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1130
global_lib_autoinst_t fr_libldap_global_config
Definition base.c:134
unlang_action_t fr_ldap_trunk_modify(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *dn, LDAPMod *mods[], LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async modification LDAP query on a trunk connection.
Definition base.c:770
fr_table_num_sorted_t const fr_ldap_tls_require_cert[]
Definition base.c:77
fr_table_num_sorted_t const fr_ldap_dereference[]
Definition base.c:86
fr_ldap_query_t * fr_ldap_search_alloc(TALLOC_CTX *ctx, char const *base_dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Allocate a new search object.
Definition base.c:1051
unlang_action_t fr_ldap_trunk_extended(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *reqoid, struct berval *reqdata, LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async LDAP "extended operation" query on a trunk connection.
Definition base.c:902
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:718
fr_table_num_sorted_t const fr_ldap_scope[]
Definition base.c:69
#define PERROR(_fmt,...)
Definition log.h:228
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:455
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:540
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:347
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RERROR(fmt,...)
Definition log.h:310
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RPERROR(fmt,...)
Definition log.h:314
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION const *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:1136
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:1884
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:109
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:124
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
int strcasecmp(char *s1, char *s2)
Definition missing.c:65
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:234
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:784
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:707
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_profiles(fr_ldap_result_code_t *ret, int *applied, rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *const *dn_list, char const *filter, fr_ldap_map_exp_t const *expanded)
Search for and apply a set of LDAP profiles.
Definition profile.c:311
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:269
static char const * url[FR_RADIUS_FAIL_MAX+1]
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG_ENABLED2()
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
static bool done
Definition radclient.c:80
static rs_t * conf
Definition radsniff.c:52
#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:244
int fr_rb_flatten_inorder(TALLOC_CTX *ctx, void **out[], fr_rb_tree_t *tree)
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_NOTFOUND
Definition rcode.h:68
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_OK
Definition rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:56
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
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:1611
tmpl_t const * tmpl
Definition rlm_ldap.c:66
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:2089
static const call_env_method_t xlat_memberof_method_env
Definition rlm_ldap.c:306
static int mod_detach(module_detach_ctx_t const *mctx)
Detach from the LDAP server and cleanup internal state.
Definition rlm_ldap.c:2471
static int mod_load(void)
Definition rlm_ldap.c:3085
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:1207
map_list_t * profile_map
List of maps to apply to the profile.
Definition rlm_ldap.c:78
#define REPEAT_LDAP_MEMBEROF_XLAT_RESULTS
Definition rlm_ldap.c:1020
static conf_parser_t profile_config[]
Definition rlm_ldap.c:101
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:1462
fr_dict_attr_t const * attr_nt_password
Definition rlm_ldap.c:358
#define LDAP_DN_SAFE_FOR
Definition rlm_ldap.c:199
static xlat_arg_parser_t const ldap_safe_xlat_arg[]
Definition rlm_ldap.c:437
ldap_auth_call_env_t * call_env
Definition rlm_ldap.c:388
static const call_env_method_t authenticate_method_env
Definition rlm_ldap.c:230
fr_ldap_result_code_t ret
Definition rlm_ldap.c:1198
global_lib_autoinst_t const * rlm_ldap_lib[]
Definition rlm_ldap.c:375
static const call_env_method_t authorize_method_env
Definition rlm_ldap.c:253
#define USERMOD_ENV(_section)
Definition rlm_ldap.c:291
fr_value_box_t password
Definition rlm_ldap.c:55
#define SSS_CONTROL_BUILD(_obj)
static xlat_arg_parser_t const ldap_uri_unescape_xlat_arg[]
Definition rlm_ldap.c:522
static const call_env_method_t xlat_profile_method_env
Definition rlm_ldap.c:328
static xlat_arg_parser_t const ldap_uri_attr_option_xlat_arg[]
Definition rlm_ldap.c:625
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:1486
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:1811
#define LDAP_FILTER_CALL_ENV_ESCAPE
Definition rlm_ldap.c:213
map_list_t const * maps
Definition rlm_ldap.c:412
static size_t profile_search_mode_table_len
Definition rlm_ldap.c:99
fr_dict_attr_t const * attr_crypt_password
Definition rlm_ldap.c:357
fr_value_box_t user_filter
Definition rlm_ldap.c:70
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:347
static int map_ctx_free(ldap_map_ctx_t *map_ctx)
Ensure map context is properly cleared up.
Definition rlm_ldap.c:1582
fr_dict_attr_t const * cache_da
Definition rlm_ldap.c:50
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:602
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:2225
fr_ldap_query_t * query
Current query performing the whoami operation.
Definition rlm_ldap.c:1365
static conf_parser_t user_config[]
Definition rlm_ldap.c:118
static fr_dict_attr_t const * attr_expr_bool_enum
Definition rlm_ldap.c:360
#define LDAP_FILTER_SAFE_FOR
Definition rlm_ldap.c:200
static fr_table_num_sorted_t const ldap_uri_scheme_table[]
Definition rlm_ldap.c:425
static xlat_arg_parser_t const ldap_xlat_arg[]
Definition rlm_ldap.c:798
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:2403
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:573
static xlat_arg_parser_t const ldap_escape_xlat_arg[]
Definition rlm_ldap.c:432
fr_dict_attr_t const * group_da
Definition rlm_ldap.c:49
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:993
fr_dict_attr_t const * attr_password
Definition rlm_ldap.c:355
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Bootstrap the module.
Definition rlm_ldap.c:3001
#define REPEAT_MOD_AUTHORIZE_RESUME
Definition rlm_ldap.c:1793
fr_value_box_t user_sasl_proxy
Definition rlm_ldap.c:59
fr_ldap_thread_trunk_t * ttrunk
Definition rlm_ldap.c:400
fr_value_box_t user_sasl_authname
Definition rlm_ldap.c:58
fr_dict_attr_t const * attr_password_with_header
Definition rlm_ldap.c:359
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:2105
rlm_ldap_t const * inst
Definition rlm_ldap.c:395
static conf_parser_t group_config[]
Definition rlm_ldap.c:137
fr_ldap_query_t * query
Definition rlm_ldap.c:414
static void mod_unload(void)
Definition rlm_ldap.c:3143
fr_dict_attr_autoload_t rlm_ldap_dict_attr[]
Definition rlm_ldap.c:363
#define LDAP_DN_CALL_ENV_ESCAPE
Definition rlm_ldap.c:202
ldap_mod_tmpl_t ** mod
Definition rlm_ldap.c:71
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:1082
char const * attr
Definition rlm_ldap.c:64
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:1031
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:1011
ssize_t expect_password_offset
Definition rlm_ldap.c:250
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:838
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:2196
static int ldap_xlat_profile_ctx_free(ldap_xlat_profile_ctx_t *to_free)
Definition rlm_ldap.c:1220
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:807
fr_dict_attr_t const * attr_cleartext_password
Definition rlm_ldap.c:356
tmpl_t const * password_tmpl
Definition rlm_ldap.c:56
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:716
fr_value_box_t user_sasl_mech
Definition rlm_ldap.c:57
fr_dict_autoload_t rlm_ldap_dict[]
Definition rlm_ldap.c:350
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Initialise thread specific data structure.
Definition rlm_ldap.c:2691
module_rlm_t rlm_ldap
Definition rlm_ldap.c:3158
char const * password
Definition rlm_ldap.c:385
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_ldap.c:1695
static int autz_ctx_free(ldap_autz_ctx_t *autz_ctx)
Ensure authorization context is properly cleared up.
Definition rlm_ldap.c:2099
ldap_schemes_t
Definition rlm_ldap.c:419
@ LDAP_SCHEME_UNIX
Definition rlm_ldap.c:420
@ LDAP_SCHEME_TCP_SSL
Definition rlm_ldap.c:422
@ LDAP_SCHEME_TCP
Definition rlm_ldap.c:421
fr_token_t op
Definition rlm_ldap.c:65
fr_value_box_t user_base
Definition rlm_ldap.c:69
fr_ldap_map_exp_t expanded
Definition rlm_ldap.c:1201
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:763
fr_ldap_query_t * query
Definition rlm_ldap.c:401
static fr_table_num_sorted_t const profile_search_mode_table[]
Definition rlm_ldap.c:94
fr_ldap_thread_t * thread
Definition rlm_ldap.c:387
static size_t ldap_uri_scheme_table_len
Definition rlm_ldap.c:430
static fr_uri_part_t const ldap_dn_parts[]
Definition rlm_ldap.c:793
static const conf_parser_t module_config[]
Definition rlm_ldap.c:156
#define USER_CALL_ENV_COMMON(_struct)
Definition rlm_ldap.c:224
ldap_usermod_call_env_t * call_env
Definition rlm_ldap.c:396
fr_value_box_t profile_filter
Filter to use when searching for users.
Definition rlm_ldap.c:77
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:2184
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Clean up thread specific data structure.
Definition rlm_ldap.c:2673
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
Definition rlm_ldap.c:2738
fr_ldap_map_exp_t expanded
Definition rlm_ldap.c:415
LDAPURLDesc * ldap_url
Definition rlm_ldap.c:413
static const call_env_parser_t sasl_call_env[]
Definition rlm_ldap.c:86
fr_value_box_t user_sasl_realm
Definition rlm_ldap.c:60
fr_value_box_list_t expanded
Definition rlm_ldap.c:402
static fr_uri_part_t const ldap_uri_parts[]
Definition rlm_ldap.c:781
fr_dict_attr_t const * user_da
Definition rlm_ldap.c:51
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:2366
static void profile_dn_list_dedupe(char const **dn_list)
Remove duplicate DNs from a NULL terminated list, keeping the first occurrence.
Definition rlm_ldap.c:1775
char const * dn
Definition rlm_ldap.c:384
static xlat_arg_parser_t const ldap_group_xlat_arg[]
Definition rlm_ldap.c:1095
rlm_ldap_t const * inst
Definition rlm_ldap.c:386
Holds state of in progress async authentication.
Definition rlm_ldap.c:383
Holds state of in progress LDAP map.
Definition rlm_ldap.c:411
Parameters to allow ldap_update_section_parse to be reused.
Definition rlm_ldap.c:248
Holds state of in progress ldap user modifications.
Definition rlm_ldap.c:394
Call environment used in the profile xlat.
Definition rlm_ldap.c:76
State of an in progress whoami extended operation.
Definition rlm_ldap.c:1364
LDAP authorization and authentication module headers.
static char const * rlm_ldap_profile_attr_select(char const *attr, char const *attr_suspend, ldap_access_state_t access_state)
Return the profile attribute matching the user's access state.
Definition rlm_ldap.h:249
@ LDAP_PROFILE_SEARCH_MODE_AUTO
Resolved at instantiation, LDAP_PROFILE_SEARCH_MODE_BULK when server side sorting is configured and t...
Definition rlm_ldap.h:24
@ LDAP_PROFILE_SEARCH_MODE_BULK
A single search retrieving every profile object, applied in result order.
Definition rlm_ldap.h:28
@ LDAP_PROFILE_SEARCH_MODE_SEQ
One search per profile DN, applied in list order.
Definition rlm_ldap.h:27
fr_ldap_map_exp_t expanded
Definition rlm_ldap.h:227
ldap_autz_call_env_t * call_env
Definition rlm_ldap.h:230
@ LDAP_ACCESS_SUSPENDED
User account has been suspended.
Definition rlm_ldap.h:218
@ LDAP_ACCESS_ALLOWED
User is allowed to login.
Definition rlm_ldap.h:216
@ LDAP_ACCESS_DISALLOWED
User it not allow to login (disabled)
Definition rlm_ldap.h:217
fr_ldap_thread_trunk_t * ttrunk
Definition rlm_ldap.h:229
rlm_ldap_t const * inst
Definition rlm_ldap.h:226
fr_value_box_t profile_filter
Filter to use when searching for profiles.
Definition rlm_ldap.h:177
fr_value_box_t user_filter
Filter to use when searching for users.
Definition rlm_ldap.h:169
LDAPMessage * entry
Definition rlm_ldap.h:231
@ LDAP_AUTZ_GROUP
Definition rlm_ldap.h:200
@ LDAP_AUTZ_POST_PROFILES
Definition rlm_ldap.h:209
@ LDAP_AUTZ_FIND
Definition rlm_ldap.h:199
@ LDAP_AUTZ_PROFILES
Definition rlm_ldap.h:208
@ LDAP_AUTZ_MAP
Definition rlm_ldap.h:207
@ LDAP_AUTZ_GROUP_PROFILES
Definition rlm_ldap.h:201
@ LDAP_AUTZ_POST_GROUP
Definition rlm_ldap.h:202
char const ** profile_dn_list
List of profile DNs to apply, default profile first, then group profiles, then profiles from the user...
Definition rlm_ldap.h:233
ldap_autz_status_t status
Definition rlm_ldap.h:232
fr_value_box_t profile_child_rdn
RDN of a child object to evaluate below each profile.
Definition rlm_ldap.h:176
fr_ldap_query_t * query
Definition rlm_ldap.h:228
ldap_access_state_t access_state
What state a user's account is in.
Definition rlm_ldap.h:240
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:168
int profiles_applied
Number of profile maps applied.
Definition rlm_ldap.h:238
char const * dn
Definition rlm_ldap.h:239
map_list_t * user_map
Attribute map applied to users and profiles.
Definition rlm_ldap.h:179
rlm_rcode_t rcode
What rcode we'll finally respond with.
Definition rlm_ldap.h:241
static char const * rlm_find_user_dn_cached(rlm_ldap_t const *inst, request_t *request)
Definition rlm_ldap.h:305
talloc_str_list_t * group_profile_dn_list
Profile DNs found in the user's group objects.
Definition rlm_ldap.h:237
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:258
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:181
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:172
@ GROUP_XLAT_MEMB_FILTER
Definition rlm_ldap.h:270
@ GROUP_XLAT_MEMB_ATTR
Definition rlm_ldap.h:271
@ GROUP_XLAT_FIND_USER
Definition rlm_ldap.h:269
talloc_str_list_t * group_dn_list
DNs of the group objects the user is a member of.
Definition rlm_ldap.h:236
module_instance_t const * dlinst
Definition rlm_ldap.h:225
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:167
Holds state of in progress async authorization.
Definition rlm_ldap.h:224
Holds state of in progress group membership check xlat.
Definition rlm_ldap.h:277
Call environment used in group membership xlat.
Definition rlm_ldap.h:188
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:433
#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:190
#define fr_sbuff_buff(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:137
static char const * section_name_str(char const *name)
Return a printable string for the section name.
Definition section.h:97
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
char const * name2
Second section name. Usually a packet type like 'access-request', 'access-accept',...
Definition section.h:45
char const * name1
First section name. Usually a verb like 'recv', 'send', etc...
Definition section.h:44
#define MODULE_THREAD_INST(_ctype)
Definition module.h:258
char const * name
Instance name e.g. user_database.
Definition module.h:357
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:351
void * data
Module's instance data.
Definition module.h:293
#define MODULE_BOOT(_ctype)
Definition module.h:256
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
void * data
Thread specific instance data.
Definition module.h:374
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:503
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
#define MODULE_INST(_ctype)
Definition module.h:257
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:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
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:496
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:546
#define talloc_get_type_abort_const
Definition talloc.h:117
char const ** strings
NULL terminated array of strings.
Definition talloc.h:254
static size_t talloc_str_list_num(talloc_str_list_t const *list)
Return the number of strings in a string list.
Definition talloc.h:268
#define talloc_asprintf
Definition talloc.h:151
#define talloc_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition talloc.h:211
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
A NULL terminated array of strings with an append cursor.
Definition talloc.h:253
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#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
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
enum fr_token fr_token_t
@ T_OP_SUB_EQ
Definition token.h:68
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_BARE_WORD
Definition token.h:118
@ T_OP_SET
Definition token.h:82
@ T_OP_ADD_EQ
Definition token.h:67
@ T_OP_CMP_FALSE
Definition token.h:103
@ T_OP_INCRM
Definition token.h:111
int module_trigger_args_build(TALLOC_CTX *ctx, fr_pair_list_t *list, CONF_SECTION const *cs, module_trigger_args_t *args)
Build trigger args pair list for modules.
Definition trigger.c:497
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:2637
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2196
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:160
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition trunk.h:159
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:543
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 consumed 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:310
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:4707
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:611
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:4196
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:4745
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:4729
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:4797
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:4838
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:4946
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:6604
@ 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:1101
#define fr_box_is_null(_x)
Definition value.h:424
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:1030
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:392
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:365
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition xlat_func.c:382
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:509
#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