2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
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.
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
20 * @brief LDAP authorization and authentication module.
22 * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 * @author Alan DeKok <aland@freeradius.org>
25 * @copyright 2012,2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
26 * @copyright 2013,2015 Network RADIUS SARL <info@networkradius.com>
27 * @copyright 2012 Alan DeKok <aland@freeradius.org>
28 * @copyright 1999-2013 The FreeRADIUS Server Project.
32 #include <freeradius-devel/rad_assert.h>
42 FR_NAME_NUMBER const ldap_scope[] = {
43 { "sub", LDAP_SCOPE_SUB },
44 { "one", LDAP_SCOPE_ONE },
45 { "base", LDAP_SCOPE_BASE },
46 #ifdef LDAP_SCOPE_CHILDREN
47 { "children", LDAP_SCOPE_CHILDREN },
52 #ifdef LDAP_OPT_X_TLS_NEVER
53 FR_NAME_NUMBER const ldap_tls_require_cert[] = {
54 { "never", LDAP_OPT_X_TLS_NEVER },
55 { "demand", LDAP_OPT_X_TLS_DEMAND },
56 { "allow", LDAP_OPT_X_TLS_ALLOW },
57 { "try", LDAP_OPT_X_TLS_TRY },
58 { "hard", LDAP_OPT_X_TLS_HARD }, /* oh yes, just like that */
64 static FR_NAME_NUMBER const ldap_dereference[] = {
65 { "never", LDAP_DEREF_NEVER },
66 { "searching", LDAP_DEREF_SEARCHING },
67 { "finding", LDAP_DEREF_FINDING },
68 { "always", LDAP_DEREF_ALWAYS },
73 static CONF_PARSER sasl_mech_dynamic[] = {
74 { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL | PW_TYPE_NOT_EMPTY, ldap_sasl_dynamic, mech), NULL },
75 { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, proxy), NULL },
76 { "realm", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, realm), NULL },
77 CONF_PARSER_TERMINATOR
80 static CONF_PARSER sasl_mech_static[] = {
81 { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_NOT_EMPTY, ldap_sasl, mech), NULL },
82 { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, proxy), NULL },
83 { "realm", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, realm), NULL },
84 CONF_PARSER_TERMINATOR
90 static CONF_PARSER tls_config[] = {
92 * Deprecated attributes
94 { "cacertfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_ca_file), NULL },
95 { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_ca_file), NULL },
97 { "cacertdir", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_ca_path), NULL },
98 { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_ca_path), NULL },
100 { "certfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_certificate_file), NULL },
101 { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_certificate_file), NULL },
103 { "keyfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_private_key_file), NULL }, // OK if it changes on HUP
104 { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_private_key_file), NULL }, // OK if it changes on HUP
106 { "randfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_random_file), NULL },
107 { "random_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_random_file), NULL },
110 * LDAP Specific TLS attributes
112 { "start_tls", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, start_tls), "no" },
113 { "require_cert", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, tls_require_cert_str), NULL },
114 CONF_PARSER_TERMINATOR
118 static CONF_PARSER profile_config[] = {
119 { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, profile_filter), "(&)" }, //!< Correct filter for when the DN is known.
120 { "attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, profile_attr), NULL },
121 { "default", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, default_profile), NULL },
122 CONF_PARSER_TERMINATOR
128 static CONF_PARSER user_config[] = {
129 { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, userobj_filter), NULL },
130 { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_scope_str), "sub" },
131 { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, userobj_base_dn), "" },
132 { "sort_by", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_sort_by), NULL },
134 { "access_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_access_attr), NULL },
135 { "access_positive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, access_positive), "yes" },
137 /* Should be deprecated */
138 { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, user_sasl), (void const *) sasl_mech_dynamic },
139 CONF_PARSER_TERMINATOR
143 * Group configuration
145 static CONF_PARSER group_config[] = {
146 { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_filter), NULL },
147 { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_scope_str), "sub" },
148 { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, groupobj_base_dn), "" },
150 { "name_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_name_attr), "cn" },
151 { "membership_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_membership_attr), NULL },
152 { "membership_filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_ldap_t, groupobj_membership_filter), NULL },
153 { "cacheable_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, cacheable_group_name), "no" },
154 { "cacheable_dn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, cacheable_group_dn), "no" },
155 { "cache_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, cache_attribute), NULL },
156 CONF_PARSER_TERMINATOR
159 static CONF_PARSER client_config[] = {
160 { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_filter), NULL },
161 { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_scope_str), "sub" },
162 { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_base_dn), "" },
163 CONF_PARSER_TERMINATOR
167 * Reference for accounting updates
169 static const CONF_PARSER acct_section_config[] = {
170 { "reference", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, ldap_acct_section_t, reference), "." },
171 CONF_PARSER_TERMINATOR
175 * Various options that don't belong in the main configuration.
177 * Note that these overlap a bit with the connection pool code!
179 static CONF_PARSER option_config[] = {
181 * Debugging flags to the server
183 { "ldap_debug", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, ldap_debug), "0x0000" },
185 { "dereference", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, dereference_str), NULL },
187 { "chase_referrals", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, chase_referrals), NULL },
189 { "rebind", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, rebind), NULL },
191 #ifdef LDAP_OPT_NETWORK_TIMEOUT
192 /* timeout on network activity */
193 { "net_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, net_timeout), "10" },
196 /* timeout for search results */
197 { "res_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, res_timeout), "20" },
199 /* allow server unlimited time for search (server-side limit) */
200 { "srv_timelimit", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, srv_timelimit), "20" },
202 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
203 { "idle", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_idle), "60" },
205 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
206 { "probes", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_probes), "3" },
208 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
209 { "interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_interval), "30" },
211 CONF_PARSER_TERMINATOR
215 static const CONF_PARSER module_config[] = {
216 { "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, config_server), NULL }, /* Do not set to required */
217 { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, rlm_ldap_t, port), NULL },
219 { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, admin_identity), NULL },
220 { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, admin_password), NULL },
222 { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, admin_sasl), (void const *) sasl_mech_static },
224 { "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, valuepair_attr), NULL },
227 /* support for eDirectory Universal Password */
228 { "edir", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, edir), NULL }, /* NULL defaults to "no" */
231 * Attempt to bind with the cleartext password we got from eDirectory
232 * Universal password for additional authorization checks.
234 { "edir_autz", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, edir_autz), NULL }, /* NULL defaults to "no" */
237 { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, do_clients), NULL }, /* NULL defaults to "no" */
239 { "user", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) user_config },
241 { "group", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) group_config },
243 { "client", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_config },
245 { "profile", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) profile_config },
247 { "options", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) option_config },
249 { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
250 CONF_PARSER_TERMINATOR
253 static ssize_t ldapquote_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
255 return rlm_ldap_escape_func(request, out, freespace, fmt, NULL);
258 /** Expand an LDAP URL into a query, and return a string result from that query.
261 static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
265 rlm_ldap_t *inst = instance;
267 LDAPURLDesc *ldap_url;
268 LDAPMessage *result = NULL;
269 LDAPMessage *entry = NULL;
271 struct berval **values;
281 if (!ldap_is_ldap_url(url)) {
282 REDEBUG("String passed does not look like an LDAP URL");
286 if (ldap_url_parse(url, &ldap_url)){
287 REDEBUG("Parsing LDAP URL failed");
292 * Nothing, empty string, "*" string, or got 2 things, die.
294 if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
295 !*ldap_url->lud_attrs[0] ||
296 (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
297 ldap_url->lud_attrs[1]) {
298 REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
303 conn = mod_conn_get(inst, request);
304 if (!conn) goto free_urldesc;
306 memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
308 status = rlm_ldap_search(&result, inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope,
309 ldap_url->lud_filter, attrs, NULL, NULL);
311 case LDAP_PROC_SUCCESS:
321 entry = ldap_first_entry(conn->handle, result);
323 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
324 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
329 values = ldap_get_values_len(conn->handle, entry, ldap_url->lud_attrs[0]);
331 RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
335 if (values[0]->bv_len >= freespace) goto free_values;
337 memcpy(out, values[0]->bv_val, values[0]->bv_len + 1); /* +1 as strlcpy expects buffer size */
338 len = values[0]->bv_len;
341 ldap_value_free_len(values);
343 ldap_msgfree(result);
345 mod_conn_release(inst, conn);
347 ldap_free_urldesc(ldap_url);
352 /** Perform LDAP-Group comparison checking
354 * Attempts to match users to groups using a variety of methods.
356 * @param instance of the rlm_ldap module.
357 * @param request Current request.
358 * @param thing Unknown.
359 * @param check Which group to check for user membership.
360 * @param check_pairs Unknown.
361 * @param reply_pairs Unknown.
363 * - 1 on failure (or if the user is not a member).
366 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
367 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
369 rlm_ldap_t *inst = instance;
375 ldap_handle_t *conn = NULL;
378 rad_assert(inst->groupobj_base_dn);
380 RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
382 if (check->vp_length == 0) {
383 REDEBUG("Cannot do comparison (group name is empty)");
388 * Check if we can do cached membership verification
390 check_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length);
394 MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue)));
395 rlm_ldap_normalise_dn(norm, check->vp_strvalue);
396 fr_pair_value_strsteal(check, norm);
398 if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
399 switch (rlm_ldap_check_cached(inst, request, check)) {
400 case RLM_MODULE_NOTFOUND:
408 * Fallback to dynamic search on failure
410 case RLM_MODULE_FAIL:
411 case RLM_MODULE_INVALID:
417 conn = mod_conn_get(inst, request);
421 * This is used in the default membership filter.
423 user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
425 mod_conn_release(inst, conn);
432 * Check groupobj user membership
434 if (inst->groupobj_membership_filter) {
435 switch (rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) {
436 case RLM_MODULE_NOTFOUND:
450 * Check userobj group membership
452 if (inst->userobj_membership_attr) {
453 switch (rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) {
454 case RLM_MODULE_NOTFOUND:
468 if (conn) mod_conn_release(inst, conn);
471 RDEBUG("User is not a member of \"%s\"", check->vp_strvalue);
479 /** Detach from the LDAP server and cleanup internal state.
482 static int mod_detach(void *instance)
484 rlm_ldap_t *inst = instance;
486 fr_connection_pool_free(inst->pool);
488 if (inst->user_map) {
489 talloc_free(inst->user_map);
493 * Keeping the dummy ld around for the lifetime
494 * of the module should always work,
495 * irrespective of what changes happen in libldap.
498 #ifdef HAVE_LDAP_UNBIND_EXT_S
499 ldap_unbind_ext_s(inst->handle, NULL, NULL);
501 ldap_unbind_s(inst->handle);
505 #ifdef HAVE_LDAP_CREATE_SORT_CONTROL
506 if (inst->userobj_sort_ctrl) ldap_control_free(inst->userobj_sort_ctrl);
512 /** Parse an accounting sub section.
514 * Allocate a new ldap_acct_section_t and write the config data into it.
516 * @param[in] inst rlm_ldap configuration.
517 * @param[in] parent of the config section.
518 * @param[out] config to write the sub section parameters to.
519 * @param[in] comp The section name were parsing the config for.
524 static int parse_sub_section(rlm_ldap_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
525 rlm_components_t comp)
529 char const *name = section_type_value[comp].section;
531 cs = cf_section_sub_find(parent, name);
533 DEBUG2("rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
534 "from this section", inst->name, name);
539 *config = talloc_zero(inst, ldap_acct_section_t);
540 if (cf_section_parse(cs, *config, acct_section_config) < 0) {
541 LDAP_ERR("Failed parsing configuration for section %s", name);
551 /** Bootstrap the module
555 * @param conf to parse.
556 * @param instance configuration data.
561 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
563 rlm_ldap_t *inst = instance;
565 inst->name = cf_section_name2(conf);
567 inst->name = cf_section_name1(conf);
571 * Group comparison checks.
573 if (cf_section_name2(conf)) {
576 snprintf(buffer, sizeof(buffer), "%s-LDAP-Group", inst->name);
578 if (paircompare_register_byname(buffer, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst) < 0) {
579 LDAP_ERR("Error registering group comparison: %s", fr_strerror());
583 inst->group_da = dict_attrbyname(buffer);
586 * We're the default instance
589 if (paircompare_register_byname("LDAP-Group", dict_attrbyvalue(PW_USER_NAME, 0),
590 false, rlm_ldap_groupcmp, inst) < 0) {
591 LDAP_ERR("Error registering group comparison: %s", fr_strerror());
595 inst->group_da = dict_attrbyname("LDAP-Group");
599 * Setup the cache attribute
601 if (inst->cache_attribute) {
604 memset(&flags, 0, sizeof(flags));
605 if (dict_addattr(inst->cache_attribute, -1, 0, PW_TYPE_STRING, flags) < 0) {
606 LDAP_ERR("Error creating cache attribute: %s", fr_strerror());
611 inst->cache_da = dict_attrbyname(inst->cache_attribute);
613 inst->cache_da = inst->group_da; /* Default to the group_da */
616 xlat_register(inst->name, ldap_xlat, rlm_ldap_escape_func, inst);
617 xlat_register("ldapquote", ldapquote_xlat, NULL, inst);
623 /** Instantiate the module
625 * Creates a new instance of the module reading parameters from a configuration section.
627 * @param conf to parse.
628 * @param instance configuration data.
633 static int mod_instantiate(CONF_SECTION *conf, void *instance)
635 static bool version_done;
640 CONF_SECTION *options, *update;
641 rlm_ldap_t *inst = instance;
645 options = cf_section_sub_find(conf, "options");
646 if (!options || !cf_pair_find(options, "chase_referrals")) {
647 inst->chase_referrals_unset = true; /* use OpenLDAP defaults */
651 * Only needs to be done once, prevents races in environment
652 * initialisation within libldap.
654 * See: https://github.com/arr2036/ldapperf/issues/2
656 #ifdef HAVE_LDAP_INITIALIZE
657 ldap_initialize(&inst->handle, "");
659 inst->handle = ldap_init("", 0);
663 * Get version info from the LDAP API.
666 static LDAPAPIInfo info = { .ldapai_info_version = LDAP_API_INFO_VERSION }; /* static to quiet valgrind about this being uninitialised */
671 ldap_errno = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
672 if (ldap_errno == LDAP_OPT_SUCCESS) {
674 * Don't generate warnings if the compile type vendor name
675 * is found within the link time vendor name.
677 * This allows the server to be built against OpenLDAP but
678 * run with Symas OpenLDAP.
680 if (strcasestr(info.ldapai_vendor_name, LDAP_VENDOR_NAME) == NULL) {
681 WARN("rlm_ldap: libldap vendor changed since the server was built");
682 WARN("rlm_ldap: linked: %s, built: %s", info.ldapai_vendor_name, LDAP_VENDOR_NAME);
685 if (info.ldapai_vendor_version < LDAP_VENDOR_VERSION) {
686 WARN("rlm_ldap: libldap older than the version the server was built against");
687 WARN("rlm_ldap: linked: %i, built: %i",
688 info.ldapai_vendor_version, LDAP_VENDOR_VERSION);
691 INFO("rlm_ldap: libldap vendor: %s, version: %i", info.ldapai_vendor_name,
692 info.ldapai_vendor_version);
694 ldap_memfree(info.ldapai_vendor_name);
695 ldap_memfree(info.ldapai_extensions);
697 DEBUG("rlm_ldap: Falling back to build time libldap version info. Query for LDAP_OPT_API_INFO "
698 "returned: %i", ldap_errno);
699 INFO("rlm_ldap: libldap vendor: %s, version: %i.%i.%i", LDAP_VENDOR_NAME,
700 LDAP_VENDOR_VERSION_MAJOR, LDAP_VENDOR_VERSION_MINOR, LDAP_VENDOR_VERSION_PATCH);
705 * If the configuration parameters can't be parsed, then fail.
707 if ((parse_sub_section(inst, conf, &inst->accounting, MOD_ACCOUNTING) < 0) ||
708 (parse_sub_section(inst, conf, &inst->postauth, MOD_POST_AUTH) < 0)) {
709 cf_log_err_cs(conf, "Failed parsing configuration");
715 * Sanity checks for cacheable groups code.
717 if (inst->cacheable_group_name && inst->groupobj_membership_filter) {
718 if (!inst->groupobj_name_attr) {
719 cf_log_err_cs(conf, "Configuration item 'group.name_attribute' must be set if cacheable "
720 "group names are enabled");
727 * If we have a *pair* as opposed to a *section*
728 * then the module is referencing another ldap module's
731 if (!cf_pair_find(conf, "pool")) {
732 if (!inst->config_server) {
733 cf_log_err_cs(conf, "Configuration item 'server' must have a value");
739 if (inst->user_sasl.mech) {
740 cf_log_err_cs(conf, "Configuration item 'user.sasl.mech' not supported. "
741 "Linked libldap does not provide ldap_sasl_bind function");
745 if (inst->admin_sasl.mech) {
746 cf_log_err_cs(conf, "Configuration item 'sasl.mech' not supported. "
747 "Linked libldap does not provide ldap_sasl_interactive_bind function");
752 #ifndef HAVE_LDAP_CREATE_SORT_CONTROL
753 if (inst->userobj_sort_by) {
754 cf_log_err_cs(conf, "Configuration item 'sort_by' not supported. "
755 "Linked libldap does not provide ldap_create_sort_control function");
761 * For backwards compatibility hack up the first 'server'
762 * CONF_ITEM into chunks, and add them back into the config.
764 * @fixme this should be removed at some point.
766 if (inst->config_server) {
775 cp = cf_pair_find(conf, "server");
777 cf_log_err_cs(conf, "Configuration item 'server' must have a value");
781 value = cf_pair_value(cp);
789 if (p == value) break; /* string contained no separators */
796 while (isspace((int) *p)) p++;
797 if (p == q) continue;
799 buff = talloc_array(inst, char, (q - p) + 1);
800 strlcpy(buff, p, talloc_array_length(buff));
804 WARN("Listing multiple LDAP servers in the 'server' configuration item "
805 "is deprecated and will be removed in a future release. "
806 "Use multiple 'server' configuration items instead");
807 WARN("- server = '%s'", value);
809 WARN("+ server = '%s'", buff);
812 * For the first instance of server we find, just replace
813 * the existing "server" config item.
816 cf_pair_replace(conf, cp, buff);
822 * For subsequent instances we need to add new conf pairs.
824 cp = cf_pair_alloc(conf, "server", buff, T_OP_EQ, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
827 ci = cf_pair_to_item(cp);
828 cf_item_add(conf, ci);
840 * Now iterate over all the 'server' config items
842 for (cp = cf_pair_find(conf, "server");
844 cp = cf_pair_find_next(conf, cp, "server")) {
847 value = cf_pair_value(cp);
849 #ifdef LDAP_CAN_PARSE_URLS
851 * Split original server value out into URI, server and port
852 * so whatever initialization function we use later will have
853 * the server information in the format it needs.
855 if (ldap_is_ldap_url(value)) {
856 LDAPURLDesc *ldap_url;
857 bool set_port_maybe = true;
858 int default_port = LDAP_PORT;
861 if (ldap_url_parse(value, &ldap_url)){
862 cf_log_err_cs(conf, "Parsing LDAP URL \"%s\" failed", value);
864 ldap_free_urldesc(ldap_url);
868 if (ldap_url->lud_dn) {
869 cf_log_err_cs(conf, "Base DN cannot be specified via server URL");
873 if (ldap_url->lud_attrs && ldap_url->lud_attrs[0]) {
874 cf_log_err_cs(conf, "Attribute list cannot be specified via server URL");
879 * ldap_url_parse sets this to base by default.
881 if (ldap_url->lud_scope != LDAP_SCOPE_BASE) {
882 cf_log_err_cs(conf, "Scope cannot be specified via server URL");
885 ldap_url->lud_scope = -1; /* Otherwise LDAP adds ?base */
888 * The public ldap_url_parse function sets the default
889 * port, so we have to discover whether a port was
890 * included ourselves.
892 if ((p = strchr(value, ']')) && (p[1] == ':')) { /* IPv6 */
893 set_port_maybe = false;
894 } else if ((p = strchr(value, ':')) && (p = strchr(p + 1, ':'))) { /* IPv4 */
895 set_port_maybe = false;
898 /* We allow extensions */
900 # ifdef HAVE_LDAP_INITIALIZE
905 * Figure out the default port from the URL
907 if (ldap_url->lud_scheme) {
908 if (strcmp(ldap_url->lud_scheme, "ldaps") == 0) {
909 if (inst->start_tls == true) {
910 cf_log_err_cs(conf, "ldaps:// scheme is not compatible "
914 default_port = LDAPS_PORT;
916 } else if (strcmp(ldap_url->lud_scheme, "ldapi") == 0) {
917 set_port_maybe = false; /* Unix socket, no port */
921 if (set_port_maybe) {
923 * URL port overrides configured port.
925 ldap_url->lud_port = inst->port;
928 * If there's no URL port, then set it to the default
929 * this is so debugging messages show explicitly
930 * the port we're connecting to.
932 if (!ldap_url->lud_port) ldap_url->lud_port = default_port;
935 url = ldap_url_desc2str(ldap_url);
937 cf_log_err_cs(conf, "Failed recombining URL components");
940 inst->server = talloc_asprintf_append(inst->server, "%s ", url);
945 * No LDAP initialize function. Can't specify a scheme.
947 if (ldap_url->lud_scheme &&
948 ((strcmp(ldap_url->lud_scheme, "ldaps") == 0) ||
949 (strcmp(ldap_url->lud_scheme, "ldapi") == 0) ||
950 (strcmp(ldap_url->lud_scheme, "cldap") == 0))) {
951 cf_log_err_cs(conf, "%s is not supported by linked libldap",
952 ldap_url->lud_scheme);
957 * URL port over-rides the configured
958 * port. But if there's no configured
959 * port, we use the hard-coded default.
961 if (set_port_maybe) {
962 ldap_url->lud_port = inst->port;
963 if (!ldap_url->lud_port) ldap_url->lud_port = default_port;
966 inst->server = talloc_asprintf_append(inst->server, "%s:%i ",
967 ldap_url->lud_host ? ldap_url->lud_host : "localhost",
971 * @todo We could set a few other top level
972 * directives using the URL, like base_dn
975 ldap_free_urldesc(ldap_url);
977 * We need to construct an LDAP URI
980 #endif /* HAVE_LDAP_URL_PARSE && HAVE_LDAP_IS_LDAP_URL && LDAP_URL_DESC2STR */
982 * If it's not an URL, or we don't have the functions necessary
983 * to break apart the URL and recombine it, then just treat
984 * server as a hostname.
987 #ifdef HAVE_LDAP_INITIALIZE
996 * We don't support URLs if the library didn't provide
997 * URL parsing functions.
999 if (strchr(value, '/')) {
1001 #ifdef LDAP_CAN_PARSE_URLS
1002 cf_log_err_cp(cp, "Invalid server value, must be in format <server>[:<port>] or "
1003 "an ldap URI (ldap|cldap|ldaps|ldapi)://<server>:<port>");
1005 cf_log_err_cp(cp, "Invalid server value, must be in format <server>[:<port>]");
1010 p = strrchr(value, ':');
1012 port = (int)strtol((p + 1), &q, 10);
1013 if ((p == value) || ((p + 1) == q) || (*q != '\0')) goto bad_server_fmt;
1016 len = strlen(value);
1018 if (port == 0) port = LDAP_PORT;
1020 inst->server = talloc_asprintf_append(inst->server, "ldap://%.*s:%i ", (int) len, value, port);
1023 * ldap_init takes port, which can be overridden by :port so
1024 * we don't need to do any parsing here.
1026 inst->server = talloc_asprintf_append(inst->server, "%s ", value);
1030 if (inst->server) inst->server[talloc_array_length(inst->server) - 2] = '\0';
1032 DEBUG4("LDAP server string: %s", inst->server);
1034 #ifdef LDAP_OPT_X_TLS_NEVER
1036 * Workaround for servers which support LDAPS but not START TLS
1038 if (inst->port == LDAPS_PORT || inst->tls_mode) {
1039 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
1046 * Convert dereference strings to enumerated constants
1048 if (inst->dereference_str) {
1049 inst->dereference = fr_str2int(ldap_dereference, inst->dereference_str, -1);
1050 if (inst->dereference < 0) {
1051 cf_log_err_cs(conf, "Invalid 'dereference' value \"%s\", expected 'never', 'searching', "
1052 "'finding' or 'always'", inst->dereference_str);
1057 #if LDAP_SET_REBIND_PROC_ARGS != 3
1059 * The 2-argument rebind doesn't take an instance variable. Our rebind function needs the instance
1060 * variable for the username, password, etc.
1062 if (inst->rebind == true) {
1063 cf_log_err_cs(conf, "Cannot use 'rebind' configuration item as this version of libldap "
1064 "does not support the API that we need");
1071 * Convert scope strings to enumerated constants
1073 inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1);
1074 if (inst->userobj_scope < 0) {
1075 cf_log_err_cs(conf, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'"
1076 #ifdef LDAP_SCOPE_CHILDREN
1077 ", 'base' or 'children'"
1081 , inst->userobj_scope_str);
1085 inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1);
1086 if (inst->groupobj_scope < 0) {
1087 cf_log_err_cs(conf, "Invalid 'group.scope' value \"%s\", expected 'sub', 'one'"
1088 #ifdef LDAP_SCOPE_CHILDREN
1089 ", 'base' or 'children'"
1093 , inst->groupobj_scope_str);
1097 inst->clientobj_scope = fr_str2int(ldap_scope, inst->clientobj_scope_str, -1);
1098 if (inst->clientobj_scope < 0) {
1099 cf_log_err_cs(conf, "Invalid 'client.scope' value \"%s\", expected 'sub', 'one'"
1100 #ifdef LDAP_SCOPE_CHILDREN
1101 ", 'base' or 'children'"
1105 , inst->clientobj_scope_str);
1109 #ifdef HAVE_LDAP_CREATE_SORT_CONTROL
1111 * Build the server side sort control for user objects
1113 if (inst->userobj_sort_by) {
1118 memcpy(&p, &inst->userobj_sort_by, sizeof(p));
1120 ret = ldap_create_sort_keylist(&keys, p);
1121 if (ret != LDAP_SUCCESS) {
1122 cf_log_err_cs(conf, "Invalid user.sort_by value \"%s\": %s",
1123 inst->userobj_sort_by, ldap_err2string(ret));
1128 * Always set the control as critical, if it's not needed
1129 * the user can comment it out...
1131 ret = ldap_create_sort_control(inst->handle, keys, 1, &inst->userobj_sort_ctrl);
1132 ldap_free_sort_keylist(keys);
1133 if (ret != LDAP_SUCCESS) {
1134 LDAP_ERR("Failed creating server sort control: %s", ldap_err2string(ret));
1140 if (inst->tls_require_cert_str) {
1141 #ifdef LDAP_OPT_X_TLS_NEVER
1143 * Convert cert strictness to enumerated constants
1145 inst->tls_require_cert = fr_str2int(ldap_tls_require_cert, inst->tls_require_cert_str, -1);
1146 if (inst->tls_require_cert < 0) {
1147 cf_log_err_cs(conf, "Invalid 'tls.require_cert' value \"%s\", expected 'never', "
1148 "'demand', 'allow', 'try' or 'hard'", inst->tls_require_cert_str);
1152 cf_log_err_cs(conf, "Modifying 'tls.require_cert' is not supported by current "
1153 "version of libldap. Please upgrade or substitute current libldap and "
1154 "rebuild this module");
1161 * Build the attribute map
1163 update = cf_section_sub_find(inst->cs, "update");
1164 if (update && (map_afrom_cs(&inst->user_map, update,
1165 PAIR_LIST_REPLY, PAIR_LIST_REQUEST, rlm_ldap_map_verify, inst,
1166 LDAP_MAX_ATTRMAP) < 0)) {
1171 * Initialize the socket pool.
1173 inst->pool = fr_connection_pool_module_init(inst->cs, inst, mod_conn_create, NULL, NULL);
1174 if (!inst->pool) goto error;
1177 * Bulk load dynamic clients.
1179 if (inst->do_clients) {
1180 CONF_SECTION *cs, *map, *tmpl;
1182 cs = cf_section_sub_find(inst->cs, "client");
1184 cf_log_err_cs(conf, "Told to load clients but no client section found");
1188 map = cf_section_sub_find(cs, "attribute");
1190 cf_log_err_cs(cs, "Told to load clients but no attribute section found");
1194 tmpl = cf_section_sub_find(cs, "template");
1196 if (rlm_ldap_client_load(inst, tmpl, map) < 0) {
1197 cf_log_err_cs(cs, "Error loading clients");
1209 static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull);
1210 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
1213 ldap_rcode_t status;
1215 rlm_ldap_t *inst = instance;
1216 ldap_handle_t *conn;
1218 char sasl_mech_buff[LDAP_MAX_DN_STR_LEN];
1219 char sasl_proxy_buff[LDAP_MAX_DN_STR_LEN];
1220 char sasl_realm_buff[LDAP_MAX_DN_STR_LEN];
1224 * Ensure that we're being passed a plain-text password, and not
1228 if (!request->username) {
1229 REDEBUG("Attribute \"User-Name\" is required for authentication");
1231 return RLM_MODULE_INVALID;
1234 if (!request->password ||
1235 (request->password->da->attr != PW_USER_PASSWORD)) {
1236 RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
1237 RWDEBUG("*********************************************");
1238 RWDEBUG("* THAT CONFIGURATION IS WRONG. DELETE IT. ");
1239 RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
1240 RWDEBUG("*********************************************");
1242 REDEBUG("Attribute \"User-Password\" is required for authentication");
1244 return RLM_MODULE_INVALID;
1247 if (request->password->vp_length == 0) {
1248 REDEBUG("Empty password supplied");
1250 return RLM_MODULE_INVALID;
1253 conn = mod_conn_get(inst, request);
1254 if (!conn) return RLM_MODULE_FAIL;
1257 * Expand dynamic SASL fields
1259 if (conn->inst->user_sasl.mech) {
1260 memset(&sasl, 0, sizeof(sasl));
1262 if (tmpl_expand(&sasl.mech, sasl_mech_buff, sizeof(sasl_mech_buff), request,
1263 conn->inst->user_sasl.mech, rlm_ldap_escape_func, inst) < 0) {
1264 REDEBUG("Failed expanding user.sasl.mech: %s", fr_strerror());
1265 rcode = RLM_MODULE_FAIL;
1269 if (conn->inst->user_sasl.proxy) {
1270 if (tmpl_expand(&sasl.proxy, sasl_proxy_buff, sizeof(sasl_proxy_buff), request,
1271 conn->inst->user_sasl.proxy, rlm_ldap_escape_func, inst) < 0) {
1272 REDEBUG("Failed expanding user.sasl.proxy: %s", fr_strerror());
1273 rcode = RLM_MODULE_FAIL;
1278 if (conn->inst->user_sasl.realm) {
1279 if (tmpl_expand(&sasl.realm, sasl_realm_buff, sizeof(sasl_realm_buff), request,
1280 conn->inst->user_sasl.realm, rlm_ldap_escape_func, inst) < 0) {
1281 REDEBUG("Failed expanding user.sasl.realm: %s", fr_strerror());
1282 rcode = RLM_MODULE_FAIL;
1288 RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
1291 * Get the DN by doing a search.
1293 dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1295 mod_conn_release(inst, conn);
1299 conn->rebound = true;
1300 status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue,
1301 conn->inst->user_sasl.mech ? &sasl : NULL, true);
1303 case LDAP_PROC_SUCCESS:
1304 rcode = RLM_MODULE_OK;
1305 RDEBUG("Bind as user \"%s\" was successful", dn);
1308 case LDAP_PROC_NOT_PERMITTED:
1309 rcode = RLM_MODULE_USERLOCK;
1312 case LDAP_PROC_REJECT:
1313 rcode = RLM_MODULE_REJECT;
1316 case LDAP_PROC_BAD_DN:
1317 rcode = RLM_MODULE_INVALID;
1320 case LDAP_PROC_NO_RESULT:
1321 rcode = RLM_MODULE_NOTFOUND;
1325 rcode = RLM_MODULE_FAIL;
1330 mod_conn_release(inst, conn);
1335 /** Search for and apply an LDAP profile
1337 * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common sets of attributes
1340 * @param[in] inst rlm_ldap configuration.
1341 * @param[in] request Current request.
1342 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
1343 * @param[in] dn of profile object to apply.
1344 * @param[in] expanded Structure containing a list of xlat expanded attribute names and mapping information.
1345 * @return One of the RLM_MODULE_* values.
1347 static rlm_rcode_t rlm_ldap_map_profile(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
1348 char const *dn, rlm_ldap_map_exp_t const *expanded)
1350 rlm_rcode_t rcode = RLM_MODULE_OK;
1351 ldap_rcode_t status;
1352 LDAPMessage *result = NULL, *entry = NULL;
1354 LDAP *handle = (*pconn)->handle;
1356 char filter_buff[LDAP_MAX_FILTER_STR_LEN];
1358 rad_assert(inst->profile_filter); /* We always have a default filter set */
1360 if (!dn || !*dn) return RLM_MODULE_OK;
1362 if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request,
1363 inst->profile_filter, rlm_ldap_escape_func, NULL) < 0) {
1364 REDEBUG("Failed creating profile filter");
1366 return RLM_MODULE_INVALID;
1369 status = rlm_ldap_search(&result, inst, request, pconn, dn,
1370 LDAP_SCOPE_BASE, filter, expanded->attrs, NULL, NULL);
1372 case LDAP_PROC_SUCCESS:
1375 case LDAP_PROC_BAD_DN:
1376 case LDAP_PROC_NO_RESULT:
1377 RDEBUG("Profile object \"%s\" not found", dn);
1378 return RLM_MODULE_NOTFOUND;
1381 return RLM_MODULE_FAIL;
1387 entry = ldap_first_entry(handle, result);
1389 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1390 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1392 rcode = RLM_MODULE_NOTFOUND;
1397 RDEBUG("Processing profile attributes");
1398 if (rlm_ldap_map_do(inst, request, handle, expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
1401 ldap_msgfree(result);
1406 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) CC_HINT(nonnull);
1407 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
1409 rlm_rcode_t rcode = RLM_MODULE_OK;
1410 ldap_rcode_t status;
1413 rlm_ldap_t *inst = instance;
1414 struct berval **values;
1416 ldap_handle_t *conn;
1417 LDAPMessage *result, *entry;
1418 char const *dn = NULL;
1419 rlm_ldap_map_exp_t expanded; /* faster than mallocing every time */
1422 * Don't be tempted to add a check for request->username
1423 * or request->password here. rlm_ldap.authorize can be used for
1424 * many things besides searching for users.
1427 if (rlm_ldap_map_expand(&expanded, request, inst->user_map) < 0) return RLM_MODULE_FAIL;
1429 conn = mod_conn_get(inst, request);
1430 if (!conn) return RLM_MODULE_FAIL;
1433 * Add any additional attributes we need for checking access, memberships, and profiles
1435 if (inst->userobj_access_attr) {
1436 expanded.attrs[expanded.count++] = inst->userobj_access_attr;
1439 if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) {
1440 expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
1443 if (inst->profile_attr) {
1444 expanded.attrs[expanded.count++] = inst->profile_attr;
1447 if (inst->valuepair_attr) {
1448 expanded.attrs[expanded.count++] = inst->valuepair_attr;
1451 expanded.attrs[expanded.count] = NULL;
1453 dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode);
1458 entry = ldap_first_entry(conn->handle, result);
1460 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1461 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1469 if (inst->userobj_access_attr) {
1470 rcode = rlm_ldap_check_access(inst, request, conn, entry);
1471 if (rcode != RLM_MODULE_OK) {
1477 * Check if we need to cache group memberships
1479 if (inst->cacheable_group_dn || inst->cacheable_group_name) {
1480 if (inst->userobj_membership_attr) {
1481 rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr);
1482 if (rcode != RLM_MODULE_OK) {
1487 rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
1488 if (rcode != RLM_MODULE_OK) {
1495 * We already have a Cleartext-Password. Skip edir.
1497 if (fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
1502 * Retrieve Universal Password if we use eDirectory
1507 size_t pass_size = sizeof(password);
1510 * Retrive universal password
1512 res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
1514 REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res));
1515 rcode = RLM_MODULE_FAIL;
1521 * Add Cleartext-Password attribute to the request
1523 vp = radius_pair_create(request, &request->config, PW_CLEARTEXT_PASSWORD, 0);
1524 fr_pair_value_strcpy(vp, password);
1525 vp->vp_length = pass_size;
1527 if (RDEBUG_ENABLED3) {
1528 RDEBUG3("Added eDirectory password. control:%s += '%s'", vp->da->name, vp->vp_strvalue);
1530 RDEBUG2("Added eDirectory password");
1533 if (inst->edir_autz) {
1534 RDEBUG2("Binding as user for eDirectory authorization checks");
1538 conn->rebound = true;
1539 status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, NULL, true);
1541 case LDAP_PROC_SUCCESS:
1542 rcode = RLM_MODULE_OK;
1543 RDEBUG("Bind as user '%s' was successful", dn);
1546 case LDAP_PROC_NOT_PERMITTED:
1547 rcode = RLM_MODULE_USERLOCK;
1550 case LDAP_PROC_REJECT:
1551 rcode = RLM_MODULE_REJECT;
1554 case LDAP_PROC_BAD_DN:
1555 rcode = RLM_MODULE_INVALID;
1558 case LDAP_PROC_NO_RESULT:
1559 rcode = RLM_MODULE_NOTFOUND;
1563 rcode = RLM_MODULE_FAIL;
1573 * Apply ONE user profile, or a default user profile.
1575 if (inst->default_profile) {
1576 char const *profile;
1577 char profile_buff[1024];
1579 if (tmpl_expand(&profile, profile_buff, sizeof(profile_buff),
1580 request, inst->default_profile, NULL, NULL) < 0) {
1581 REDEBUG("Failed creating default profile string");
1583 rcode = RLM_MODULE_INVALID;
1587 switch (rlm_ldap_map_profile(inst, request, &conn, profile, &expanded)) {
1588 case RLM_MODULE_INVALID:
1589 rcode = RLM_MODULE_INVALID;
1592 case RLM_MODULE_FAIL:
1593 rcode = RLM_MODULE_FAIL;
1596 case RLM_MODULE_UPDATED:
1597 rcode = RLM_MODULE_UPDATED;
1605 * Apply a SET of user profiles.
1607 if (inst->profile_attr) {
1608 values = ldap_get_values_len(conn->handle, entry, inst->profile_attr);
1609 if (values != NULL) {
1610 for (i = 0; values[i] != NULL; i++) {
1614 value = rlm_ldap_berval_to_string(request, values[i]);
1615 ret = rlm_ldap_map_profile(inst, request, &conn, value, &expanded);
1617 if (ret == RLM_MODULE_FAIL) {
1618 ldap_value_free_len(values);
1624 ldap_value_free_len(values);
1628 if (inst->user_map || inst->valuepair_attr) {
1629 RDEBUG("Processing user attributes");
1630 if (rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
1631 rlm_ldap_check_reply(inst, request);
1635 talloc_free(expanded.ctx);
1636 if (result) ldap_msgfree(result);
1637 mod_conn_release(inst, conn);
1642 /** Modify user's object in LDAP
1644 * Process a modifcation map to update a user object in the LDAP directory.
1646 * @param inst rlm_ldap instance.
1647 * @param request Current request.
1648 * @param section that holds the map to process.
1649 * @return one of the RLM_MODULE_* values.
1651 static rlm_rcode_t user_modify(rlm_ldap_t *inst, REQUEST *request, ldap_acct_section_t *section)
1653 rlm_rcode_t rcode = RLM_MODULE_OK;
1654 ldap_rcode_t status;
1656 ldap_handle_t *conn = NULL;
1658 LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
1659 LDAPMod **modify = mod_p;
1661 char *passed[LDAP_MAX_ATTRMAP * 2];
1662 int i, total = 0, last_pass = 0;
1664 char *expanded[LDAP_MAX_ATTRMAP];
1672 * Build our set of modifications using the update sections in
1679 char path[MAX_STRING_LEN];
1683 rad_assert(section);
1686 * Locate the update section were going to be using
1688 if (section->reference[0] != '.') {
1692 if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) {
1696 ci = cf_reference_item(NULL, section->cs, path);
1701 if (!cf_item_is_section(ci)){
1702 REDEBUG("Reference must resolve to a section");
1707 cs = cf_section_sub_find(cf_item_to_section(ci), "update");
1709 REDEBUG("Section must contain 'update' subsection");
1715 * Iterate over all the pairs, building our mods array
1717 for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
1718 bool do_xlat = false;
1720 if (total == LDAP_MAX_ATTRMAP) {
1721 REDEBUG("Modify map size exceeded");
1726 if (!cf_item_is_pair(ci)) {
1727 REDEBUG("Entry is not in \"ldap-attribute = value\" format");
1733 * Retrieve all the information we need about the pair
1735 cp = cf_item_to_pair(ci);
1736 value = cf_pair_value(cp);
1737 attr = cf_pair_attr(cp);
1738 op = cf_pair_operator(cp);
1740 if (!value || (*value == '\0')) {
1741 RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
1746 switch (cf_pair_value_type(cp)) {
1748 case T_SINGLE_QUOTED_STRING:
1751 case T_BACK_QUOTED_STRING:
1752 case T_DOUBLE_QUOTED_STRING:
1761 if (op == T_OP_CMP_FALSE) {
1762 passed[last_pass] = NULL;
1763 } else if (do_xlat) {
1766 if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) {
1767 RDEBUG("Skipping attribute \"%s\"", attr);
1774 expanded[last_exp++] = exp;
1775 passed[last_pass] = exp;
1780 memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
1783 passed[last_pass + 1] = NULL;
1785 mod_s[total].mod_values = &(passed[last_pass]);
1791 * T_OP_EQ is *NOT* supported, it is impossible to
1792 * support because of the lack of transactions in LDAP
1795 mod_s[total].mod_op = LDAP_MOD_ADD;
1799 mod_s[total].mod_op = LDAP_MOD_REPLACE;
1803 case T_OP_CMP_FALSE:
1804 mod_s[total].mod_op = LDAP_MOD_DELETE;
1807 #ifdef LDAP_MOD_INCREMENT
1809 mod_s[total].mod_op = LDAP_MOD_INCREMENT;
1813 REDEBUG("Operator '%s' is not supported for LDAP modify operations",
1814 fr_int2str(fr_tokens, op, "<INVALID>"));
1820 * Now we know the value is ok, copy the pointers into
1821 * the ldapmod struct.
1823 memcpy(&(mod_s[total].mod_type), &attr, sizeof(mod_s[total].mod_type));
1825 mod_p[total] = &(mod_s[total]);
1830 rcode = RLM_MODULE_NOOP;
1834 mod_p[total] = NULL;
1836 conn = mod_conn_get(inst, request);
1837 if (!conn) return RLM_MODULE_FAIL;
1840 dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1841 if (!dn || (rcode != RLM_MODULE_OK)) {
1845 status = rlm_ldap_modify(inst, request, &conn, dn, modify);
1847 case LDAP_PROC_SUCCESS:
1850 case LDAP_PROC_REJECT:
1851 case LDAP_PROC_BAD_DN:
1852 rcode = RLM_MODULE_INVALID;
1856 rcode = RLM_MODULE_FAIL;
1863 * Free up any buffers we allocated for xlat expansion
1865 for (i = 0; i < last_exp; i++) {
1866 talloc_free(expanded[i]);
1869 mod_conn_release(inst, conn);
1874 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) CC_HINT(nonnull);
1875 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
1877 rlm_ldap_t *inst = instance;
1879 if (inst->accounting) return user_modify(inst, request, inst->accounting);
1881 return RLM_MODULE_NOOP;
1884 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull);
1885 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
1887 rlm_ldap_t *inst = instance;
1889 if (inst->postauth) {
1890 return user_modify(inst, request, inst->postauth);
1893 return RLM_MODULE_NOOP;
1897 /* globally exported name */
1898 extern module_t rlm_ldap;
1899 module_t rlm_ldap = {
1900 .magic = RLM_MODULE_INIT,
1902 .inst_size = sizeof(rlm_ldap_t),
1903 .config = module_config,
1904 .bootstrap = mod_bootstrap,
1905 .instantiate = mod_instantiate,
1906 .detach = mod_detach,
1908 [MOD_AUTHENTICATE] = mod_authenticate,
1909 [MOD_AUTHORIZE] = mod_authorize,
1910 [MOD_ACCOUNTING] = mod_accounting,
1911 [MOD_POST_AUTH] = mod_post_auth