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, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief LDAP authorization and authentication module.
21 * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 * @author Alan DeKok <aland@freeradius.org>
24 * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
25 * @copyright 2012-2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
26 * @copyright 2012 Alan DeKok <aland@freeradius.org>
27 * @copyright 1999-2013 The FreeRADIUS Server Project.
31 #include <freeradius-devel/rad_assert.h>
41 FR_NAME_NUMBER const ldap_scope[] = {
42 { "sub", LDAP_SCOPE_SUB },
43 { "one", LDAP_SCOPE_ONE },
44 { "base", LDAP_SCOPE_BASE },
45 { "children", LDAP_SCOPE_CHILDREN },
50 #ifdef LDAP_OPT_X_TLS_NEVER
51 FR_NAME_NUMBER const ldap_tls_require_cert[] = {
52 { "never", LDAP_OPT_X_TLS_NEVER },
53 { "demand", LDAP_OPT_X_TLS_DEMAND },
54 { "allow", LDAP_OPT_X_TLS_ALLOW },
55 { "try", LDAP_OPT_X_TLS_TRY },
56 { "hard", LDAP_OPT_X_TLS_HARD }, /* oh yes, just like that */
65 static CONF_PARSER tls_config[] = {
67 * Deprecated attributes
69 {"cacertfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_ca_file), NULL, NULL},
70 {"ca_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_ca_file), NULL, NULL},
72 {"cacertdir", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_ca_path), NULL, NULL},
73 {"ca_path", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_ca_path), NULL, NULL},
75 {"certfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_certificate_file), NULL, NULL},
76 {"certificate_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_certificate_file), NULL, NULL},
78 {"keyfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_private_key_file), NULL, NULL}, // OK if it changes on HUP
79 {"private_key_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_private_key_file), NULL, NULL}, // OK if it changes on HUP
81 {"randfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_random_file), NULL, NULL},
82 {"random_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_random_file), NULL, NULL},
85 * LDAP Specific TLS attributes
87 {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, start_tls), NULL, "no"},
88 {"require_cert", PW_TYPE_STRING, offsetof(ldap_instance_t, tls_require_cert_str), NULL, NULL},
90 { NULL, -1, 0, NULL, NULL }
94 static CONF_PARSER profile_config[] = {
95 {"filter", PW_TYPE_STRING, offsetof(ldap_instance_t, profile_filter), NULL, "(&)"}, //!< Correct filter for
98 {"attribute", PW_TYPE_STRING, offsetof(ldap_instance_t, profile_attr), NULL, NULL},
99 {"default", PW_TYPE_STRING, offsetof(ldap_instance_t, default_profile), NULL, NULL},
101 { NULL, -1, 0, NULL, NULL }
107 static CONF_PARSER user_config[] = {
108 {"filter", PW_TYPE_STRING, offsetof(ldap_instance_t, userobj_filter), NULL, "(uid=%u)"},
109 {"scope", PW_TYPE_STRING, offsetof(ldap_instance_t, userobj_scope_str), NULL, "sub"},
110 {"base_dn", PW_TYPE_STRING, offsetof(ldap_instance_t,userobj_base_dn), NULL, ""},
112 {"access_attribute", PW_TYPE_STRING, offsetof(ldap_instance_t, userobj_access_attr), NULL, NULL},
113 {"access_positive", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, access_positive), NULL, "yes"},
115 { NULL, -1, 0, NULL, NULL }
119 * Group configuration
121 static CONF_PARSER group_config[] = {
122 {"filter", PW_TYPE_STRING, offsetof(ldap_instance_t, groupobj_filter), NULL, NULL},
123 {"scope", PW_TYPE_STRING, offsetof(ldap_instance_t, groupobj_scope_str), NULL, "sub"},
124 {"base_dn", PW_TYPE_STRING, offsetof(ldap_instance_t, groupobj_base_dn), NULL, ""},
126 {"name_attribute", PW_TYPE_STRING, offsetof(ldap_instance_t, groupobj_name_attr), NULL, "cn"},
127 {"membership_attribute", PW_TYPE_STRING, offsetof(ldap_instance_t, userobj_membership_attr), NULL, NULL},
128 {"membership_filter", PW_TYPE_STRING, offsetof(ldap_instance_t, groupobj_membership_filter), NULL, NULL},
129 {"cacheable_name", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_name), NULL, "no"},
130 {"cacheable_dn", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_dn), NULL, "no"},
132 { NULL, -1, 0, NULL, NULL }
136 * Client configuration
138 static CONF_PARSER client_attribute[] = {
139 {"identifier", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_identifier), NULL, "host"},
140 {"shortname", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_shortname), NULL, "cn"},
141 {"nas_type", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_type), NULL, NULL},
142 {"secret", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_secret), NULL, NULL},
143 {"virtual_server", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_server), NULL, NULL},
144 {"require_message_authenticator", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_require_ma),
147 { NULL, -1, 0, NULL, NULL }
150 static CONF_PARSER client_config[] = {
151 {"filter", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_filter), NULL, NULL},
152 {"scope", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_scope_str), NULL, "sub"},
153 {"base_dn", PW_TYPE_STRING, offsetof(ldap_instance_t, clientobj_base_dn), NULL, ""},
154 { "attribute", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_attribute },
156 { NULL, -1, 0, NULL, NULL }
160 * Reference for accounting updates
162 static const CONF_PARSER acct_section_config[] = {
163 {"reference", PW_TYPE_STRING, offsetof(ldap_acct_section_t, reference), NULL, "."},
165 {NULL, -1, 0, NULL, NULL}
169 * Various options that don't belong in the main configuration.
171 * Note that these overlap a bit with the connection pool code!
173 static CONF_PARSER option_config[] = {
175 * Debugging flags to the server
177 {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance_t,ldap_debug), NULL, "0x0000"},
179 {"chase_referrals", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,chase_referrals), NULL, NULL},
181 {"rebind", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,rebind), NULL, NULL},
183 /* timeout on network activity */
184 {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,net_timeout), NULL, "10"},
186 /* timeout for search results */
187 {"res_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,res_timeout), NULL, "20"},
189 /* allow server unlimited time for search (server-side limit) */
190 {"srv_timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance_t,srv_timelimit), NULL, "20"},
192 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
193 {"idle", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_idle), NULL, "60"},
195 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
196 {"probes", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_probes), NULL, "3"},
198 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
199 {"interval", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_interval), NULL, "30"},
202 { NULL, -1, 0, NULL, NULL }
206 static const CONF_PARSER module_config[] = {
207 {"server", PW_TYPE_STRING | PW_TYPE_REQUIRED, offsetof(ldap_instance_t,server), NULL, "localhost"},
208 {"port", PW_TYPE_INTEGER, offsetof(ldap_instance_t,port), NULL, "389"},
210 {"password", PW_TYPE_STRING | PW_TYPE_SECRET, offsetof(ldap_instance_t,password), NULL, ""},
211 {"identity", PW_TYPE_STRING, offsetof(ldap_instance_t,admin_dn), NULL, ""},
213 {"valuepair_attribute", PW_TYPE_STRING, offsetof(ldap_instance_t, valuepair_attr), NULL, NULL},
216 /* support for eDirectory Universal Password */
217 {"edir", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir), NULL, NULL}, /* NULL defaults to "no" */
220 * Attempt to bind with the cleartext password we got from eDirectory
221 * Universal password for additional authorization checks.
223 {"edir_autz", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
226 {"read_clients", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,do_clients), NULL, NULL}, /* NULL defaults to "no" */
228 { "user", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) user_config },
230 { "group", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) group_config },
232 { "client", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_config },
234 { "profile", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) profile_config },
236 { "options", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) option_config },
238 { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
240 {NULL, -1, 0, NULL, NULL}
243 /** Expand an LDAP URL into a query, and return a string result from that query.
246 static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
250 ldap_instance_t *inst = instance;
251 LDAPURLDesc *ldap_url;
252 LDAPMessage *result = NULL;
253 LDAPMessage *entry = NULL;
262 if (!ldap_is_ldap_url(url)) {
263 REDEBUG("String passed does not look like an LDAP URL");
267 if (ldap_url_parse(url, &ldap_url)){
268 REDEBUG("Parsing LDAP URL failed");
273 * Nothing, empty string, "*" string, or got 2 things, die.
275 if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
276 !*ldap_url->lud_attrs[0] ||
277 (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
278 ldap_url->lud_attrs[1]) {
279 REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
284 if (ldap_url->lud_host &&
285 ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
286 (ldap_url->lud_port != inst->port))) {
287 RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
292 conn = rlm_ldap_get_socket(inst, request);
293 if (!conn) goto free_urldesc;
295 memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
297 status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
300 case LDAP_PROC_SUCCESS:
302 case LDAP_PROC_NO_RESULT:
303 RDEBUG("Search returned not found");
311 entry = ldap_first_entry(conn->handle, result);
313 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
314 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
319 vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
321 RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
325 len = strlen(vals[0]);
326 if (len >= freespace){
330 strlcpy(out, vals[0], freespace);
333 ldap_value_free(vals);
335 ldap_msgfree(result);
337 rlm_ldap_release_socket(inst, conn);
339 ldap_free_urldesc(ldap_url);
344 /** Perform LDAP-Group comparison checking
346 * Attempts to match users to groups using a variety of methods.
348 * @param instance of the rlm_ldap module.
349 * @param request Current request.
350 * @param thing Unknown.
351 * @param check Which group to check for user membership.
352 * @param check_pairs Unknown.
353 * @param reply_pairs Unknown.
354 * @return 1 on failure (or if the user is not a member), else 0.
356 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
357 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
359 ldap_instance_t *inst = instance;
365 ldap_handle_t *conn = NULL;
368 rad_assert(inst->groupobj_base_dn);
370 RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
372 if (check->length == 0) {
373 RDEBUG("Cannot do comparison (group name is empty)");
378 * Check if we can do cached membership verification
380 check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
381 if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
382 switch(rlm_ldap_check_cached(inst, request, check)) {
383 case RLM_MODULE_NOTFOUND:
390 * Fallback to dynamic search on failure
392 case RLM_MODULE_FAIL:
393 case RLM_MODULE_INVALID:
399 conn = rlm_ldap_get_socket(inst, request);
403 * This is used in the default membership filter.
405 user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
407 rlm_ldap_release_socket(inst, conn);
414 * Check groupobj user membership
416 if (inst->groupobj_membership_filter) {
417 switch(rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) {
418 case RLM_MODULE_NOTFOUND:
430 * Check userobj group membership
432 if (inst->userobj_membership_attr) {
433 switch(rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) {
434 case RLM_MODULE_NOTFOUND:
447 rlm_ldap_release_socket(inst, conn);
451 RDEBUG("User is not a member of specified group");
459 /** Detach from the LDAP server and cleanup internal state.
462 static int mod_detach(void *instance)
464 ldap_instance_t *inst = instance;
466 fr_connection_pool_delete(inst->pool);
468 if (inst->user_map) {
469 talloc_free(inst->user_map);
475 /** Parse an accounting sub section.
477 * Allocate a new ldap_acct_section_t and write the config data into it.
479 * @param[in] inst rlm_ldap configuration.
480 * @param[in] parent of the config section.
481 * @param[out] config to write the sub section parameters to.
482 * @param[in] comp The section name were parsing the config for.
483 * @return 0 on success, else < 0 on failure.
485 static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
486 rlm_components_t comp)
490 char const *name = section_type_value[comp].section;
492 cs = cf_section_sub_find(parent, name);
494 INFO("rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
495 "from this section", inst->xlat_name, name);
500 *config = talloc_zero(inst, ldap_acct_section_t);
501 if (cf_section_parse(cs, *config, acct_section_config) < 0) {
502 LDAP_ERR("Failed parsing configuration for section %s", name);
512 /** Instantiate the module
514 * Creates a new instance of the module reading parameters from a configuration section.
516 * @param conf to parse.
517 * @param instance Where to write pointer to configuration data.
518 * @return 0 on success < 0 on failure.
520 static int mod_instantiate(CONF_SECTION *conf, void *instance)
522 CONF_SECTION *options;
523 ldap_instance_t *inst = instance;
527 options = cf_section_sub_find(conf, "options");
528 if (!options || !cf_pair_find(options, "chase_referrals")) {
529 inst->chase_referrals_unset = true; /* use OpenLDAP defaults */
532 inst->xlat_name = cf_section_name2(conf);
533 if (!inst->xlat_name) {
534 inst->xlat_name = cf_section_name1(conf);
538 * If the configuration parameters can't be parsed, then fail.
540 if ((parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
541 (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
542 LDAP_ERR("Failed parsing configuration");
548 * Sanity checks for cacheable groups code.
550 if (inst->cacheable_group_name && inst->groupobj_membership_filter) {
551 if (!inst->groupobj_name_attr) {
552 LDAP_ERR("Directive 'group.name_attribute' must be set if cacheable group names are enabled");
559 * Check for URLs. If they're used and the library doesn't support them, then complain.
562 if (ldap_is_ldap_url(inst->server)) {
563 #ifdef HAVE_LDAP_INITIALIZE
567 LDAP_ERR("Directive 'server' is in URL form but ldap_initialize() is not available");
573 * Workaround for servers which support LDAPS but not START TLS
575 if (inst->port == LDAPS_PORT || inst->tls_mode) {
576 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
581 #if LDAP_SET_REBIND_PROC_ARGS != 3
583 * The 2-argument rebind doesn't take an instance variable. Our rebind function needs the instance
584 * variable for the username, password, etc.
586 if (inst->rebind == true) {
587 LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API "
595 * Convert scope strings to enumerated constants
597 inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1);
598 if (inst->userobj_scope < 0) {
599 LDAP_ERR("Invalid 'user.scope' value \"%s\", expected 'sub', 'one', 'base' or 'children'",
600 inst->userobj_scope_str);
604 inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1);
605 if (inst->groupobj_scope < 0) {
606 LDAP_ERR("Invalid 'group.scope' value \"%s\", expected 'sub', 'one', 'base' or 'children'",
607 inst->groupobj_scope_str);
611 inst->clientobj_scope = fr_str2int(ldap_scope, inst->clientobj_scope_str, -1);
612 if (inst->clientobj_scope < 0) {
613 LDAP_ERR("Invalid 'client.scope' value \"%s\", expected 'sub', 'one', 'base' or 'children'",
614 inst->clientobj_scope_str);
618 if (inst->tls_require_cert_str) {
619 #ifdef LDAP_OPT_X_TLS_NEVER
621 * Convert cert strictness to enumerated constants
623 inst->tls_require_cert = fr_str2int(ldap_tls_require_cert, inst->tls_require_cert_str, -1);
624 if (inst->tls_require_cert < 0) {
625 LDAP_ERR("Invalid 'tls.require_cert' value \"%s\", expected 'never', 'demand', 'allow', "
626 "'try' or 'hard'", inst->tls_require_cert_str);
630 LDAP_ERR("Modifying 'tls.require_cert' is not supported by current version of libldap. "
631 "Please upgrade libldap and rebuild this module");
637 * Build the attribute map
639 if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
644 * Group comparison checks.
646 if (cf_section_name2(conf)) {
650 snprintf(buffer, sizeof(buffer), "%s-Ldap-Group",
652 memset(&flags, 0, sizeof(flags));
654 dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
655 inst->group_da = dict_attrbyname(buffer);
656 if (!inst->group_da) {
657 LDAP_ERR("Failed creating attribute %s", buffer);
662 paircompare_register(inst->group_da, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst);
664 * Were the default instance
667 inst->group_da = dict_attrbyvalue(PW_LDAP_GROUP, 0);
668 paircompare_register(dict_attrbyvalue(PW_LDAP_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0),
669 false, rlm_ldap_groupcmp, inst);
672 xlat_register(inst->xlat_name, ldap_xlat, rlm_ldap_escape_func, inst);
675 * Initialize the socket pool.
677 inst->pool = fr_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, mod_conn_delete, NULL);
683 * Bulk load dynamic clients.
685 if (inst->do_clients) {
686 if (rlm_ldap_load_clients(inst) < 0) {
687 LDAP_ERR("Error loading clients");
699 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
704 ldap_instance_t *inst = instance;
708 * Ensure that we're being passed a plain-text password, and not
712 if (!request->username) {
713 REDEBUG("Attribute \"User-Name\" is required for authentication");
715 return RLM_MODULE_INVALID;
718 if (!request->password ||
719 (request->password->da->attr != PW_USER_PASSWORD)) {
720 RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
721 RWDEBUG("*********************************************");
722 RWDEBUG("* THAT CONFIGURATION IS WRONG. DELETE IT. ");
723 RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
724 RWDEBUG("*********************************************");
726 REDEBUG("Attribute \"User-Password\" is required for authentication");
728 return RLM_MODULE_INVALID;
731 if (request->password->length == 0) {
732 REDEBUG("Empty password supplied");
734 return RLM_MODULE_INVALID;
737 RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
739 conn = rlm_ldap_get_socket(inst, request);
740 if (!conn) return RLM_MODULE_FAIL;
743 * Get the DN by doing a search.
745 dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
747 rlm_ldap_release_socket(inst, conn);
755 conn->rebound = true;
756 status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, true);
758 case LDAP_PROC_SUCCESS:
759 rcode = RLM_MODULE_OK;
760 RDEBUG("Bind as user \"%s\" was successful", dn);
763 case LDAP_PROC_NOT_PERMITTED:
764 rcode = RLM_MODULE_USERLOCK;
767 case LDAP_PROC_REJECT:
768 rcode = RLM_MODULE_REJECT;
771 case LDAP_PROC_BAD_DN:
772 rcode = RLM_MODULE_INVALID;
775 case LDAP_PROC_NO_RESULT:
776 rcode = RLM_MODULE_NOTFOUND;
780 rcode = RLM_MODULE_FAIL;
784 rlm_ldap_release_socket(inst, conn);
789 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
791 rlm_rcode_t rcode = RLM_MODULE_OK;
795 ldap_instance_t *inst = instance;
799 LDAPMessage *result, *entry;
800 char const *dn = NULL;
801 rlm_ldap_map_xlat_t expanded; /* faster than mallocing every time */
803 if (!request->username) {
804 RDEBUG2("Attribute \"User-Name\" is required for authorization");
806 return RLM_MODULE_NOOP;
810 * Check for valid input, zero length names not permitted
812 if (request->username->length == 0) {
813 RDEBUG2("Zero length username not permitted");
815 return RLM_MODULE_INVALID;
818 if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
819 return RLM_MODULE_FAIL;
822 conn = rlm_ldap_get_socket(inst, request);
823 if (!conn) return RLM_MODULE_FAIL;
826 * Add any additional attributes we need for checking access, memberships, and profiles
828 if (inst->userobj_access_attr) {
829 expanded.attrs[expanded.count++] = inst->userobj_access_attr;
832 if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) {
833 expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
836 if (inst->profile_attr) {
837 expanded.attrs[expanded.count++] = inst->profile_attr;
840 if (inst->valuepair_attr) {
841 expanded.attrs[expanded.count++] = inst->valuepair_attr;
844 expanded.attrs[expanded.count] = NULL;
846 dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode);
851 entry = ldap_first_entry(conn->handle, result);
853 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
854 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
862 if (inst->userobj_access_attr) {
863 rcode = rlm_ldap_check_access(inst, request, conn, entry);
864 if (rcode != RLM_MODULE_OK) {
870 * Check if we need to cache group memberships
872 if (inst->cacheable_group_dn || inst->cacheable_group_name) {
873 if (inst->userobj_membership_attr) {
874 rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr);
875 if (rcode != RLM_MODULE_OK) {
880 rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
881 if (rcode != RLM_MODULE_OK) {
888 * We already have a Cleartext-Password. Skip edir.
890 if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
895 * Retrieve Universal Password if we use eDirectory
900 size_t pass_size = sizeof(password);
903 * Retrive universal password
905 res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
907 REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res));
908 rcode = RLM_MODULE_FAIL;
914 * Add Cleartext-Password attribute to the request
916 vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
917 pairstrcpy(vp, password);
918 vp->length = pass_size;
920 if (RDEBUG_ENABLED3) {
921 RDEBUG3("Added eDirectory password. control:%s += '%s'", vp->da->name, vp->vp_strvalue);
923 RDEBUG2("Added eDirectory password");
926 if (inst->edir_autz) {
927 RDEBUG2("Binding as user for eDirectory authorization checks");
931 conn->rebound = true;
932 status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true);
934 case LDAP_PROC_SUCCESS:
935 rcode = RLM_MODULE_OK;
936 RDEBUG("Bind as user '%s' was successful", dn);
939 case LDAP_PROC_NOT_PERMITTED:
940 rcode = RLM_MODULE_USERLOCK;
943 case LDAP_PROC_REJECT:
944 rcode = RLM_MODULE_REJECT;
947 case LDAP_PROC_BAD_DN:
948 rcode = RLM_MODULE_INVALID;
951 case LDAP_PROC_NO_RESULT:
952 rcode = RLM_MODULE_NOTFOUND;
956 rcode = RLM_MODULE_FAIL;
966 * Apply ONE user profile, or a default user profile.
968 if (inst->default_profile) {
971 if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) {
972 REDEBUG("Failed creating default profile string");
974 rcode = RLM_MODULE_INVALID;
978 rlm_ldap_map_profile(inst, request, &conn, profile, &expanded);
982 * Apply a SET of user profiles.
984 if (inst->profile_attr) {
985 vals = ldap_get_values(conn->handle, entry, inst->profile_attr);
987 for (i = 0; vals[i] != NULL; i++) {
988 rlm_ldap_map_profile(inst, request, &conn, vals[i], &expanded);
991 ldap_value_free(vals);
995 if (inst->user_map || inst->valuepair_attr) {
996 RDEBUG("Processing user attributes");
997 rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
998 rlm_ldap_check_reply(inst, request);
1002 rlm_ldap_map_xlat_free(&expanded);
1004 ldap_msgfree(result);
1006 rlm_ldap_release_socket(inst, conn);
1011 /** Modify user's object in LDAP
1013 * Process a modifcation map to update a user object in the LDAP directory.
1015 * @param inst rlm_ldap instance.
1016 * @param request Current request.
1017 * @param section that holds the map to process.
1018 * @return one of the RLM_MODULE_* values.
1020 static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
1022 rlm_rcode_t rcode = RLM_MODULE_OK;
1023 ldap_rcode_t status;
1025 ldap_handle_t *conn = NULL;
1027 LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
1028 LDAPMod **modify = mod_p;
1030 char *passed[LDAP_MAX_ATTRMAP * 2];
1031 int i, total = 0, last_pass = 0;
1033 char *expanded[LDAP_MAX_ATTRMAP];
1041 * Build our set of modifications using the update sections in
1048 char path[MAX_STRING_LEN];
1052 rad_assert(section);
1055 * Locate the update section were going to be using
1057 if (section->reference[0] != '.') {
1061 if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) {
1065 ci = cf_reference_item(NULL, section->cs, path);
1070 if (!cf_item_is_section(ci)){
1071 REDEBUG("Reference must resolve to a section");
1076 cs = cf_section_sub_find(cf_itemtosection(ci), "update");
1078 REDEBUG("Section must contain 'update' subsection");
1084 * Iterate over all the pairs, building our mods array
1086 for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
1087 bool do_xlat = false;
1089 if (total == LDAP_MAX_ATTRMAP) {
1090 REDEBUG("Modify map size exceeded");
1095 if (!cf_item_is_pair(ci)) {
1096 REDEBUG("Entry is not in \"ldap-attribute = value\" format");
1102 * Retrieve all the information we need about the pair
1104 cp = cf_itemtopair(ci);
1105 value = cf_pair_value(cp);
1106 attr = cf_pair_attr(cp);
1107 op = cf_pair_operator(cp);
1109 if (!value || (*value == '\0')) {
1110 RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
1115 switch (cf_pair_value_type(cp))
1118 case T_SINGLE_QUOTED_STRING:
1120 case T_BACK_QUOTED_STRING:
1121 case T_DOUBLE_QUOTED_STRING:
1129 if (op == T_OP_CMP_FALSE) {
1130 passed[last_pass] = NULL;
1131 } else if (do_xlat) {
1134 if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) {
1135 RDEBUG("Skipping attribute \"%s\"", attr);
1142 expanded[last_exp++] = exp;
1143 passed[last_pass] = exp;
1148 memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
1151 passed[last_pass + 1] = NULL;
1153 mod_s[total].mod_values = &(passed[last_pass]);
1160 * T_OP_EQ is *NOT* supported, it is impossible to
1161 * support because of the lack of transactions in LDAP
1164 mod_s[total].mod_op = LDAP_MOD_ADD;
1168 mod_s[total].mod_op = LDAP_MOD_REPLACE;
1172 case T_OP_CMP_FALSE:
1173 mod_s[total].mod_op = LDAP_MOD_DELETE;
1176 #ifdef LDAP_MOD_INCREMENT
1178 mod_s[total].mod_op = LDAP_MOD_INCREMENT;
1182 REDEBUG("Operator '%s' is not supported for LDAP modify operations",
1183 fr_int2str(fr_tokens, op, "<INVALID>"));
1189 * Now we know the value is ok, copy the pointers into
1190 * the ldapmod struct.
1192 memcpy(&(mod_s[total].mod_type), &(attr), sizeof(mod_s[total].mod_type));
1194 mod_p[total] = &(mod_s[total]);
1199 rcode = RLM_MODULE_NOOP;
1203 mod_p[total] = NULL;
1205 conn = rlm_ldap_get_socket(inst, request);
1206 if (!conn) return RLM_MODULE_FAIL;
1209 dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1210 if (!dn || (rcode != RLM_MODULE_OK)) {
1214 status = rlm_ldap_modify(inst, request, &conn, dn, modify);
1216 case LDAP_PROC_SUCCESS:
1219 case LDAP_PROC_REJECT:
1220 case LDAP_PROC_BAD_DN:
1221 rcode = RLM_MODULE_INVALID;
1225 rcode = RLM_MODULE_FAIL;
1232 * Free up any buffers we allocated for xlat expansion
1234 for (i = 0; i < last_exp; i++) {
1235 talloc_free(expanded[i]);
1238 rlm_ldap_release_socket(inst, conn);
1243 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
1244 ldap_instance_t *inst = instance;
1246 if (inst->accounting) {
1247 return user_modify(inst, request, inst->accounting);
1250 return RLM_MODULE_NOOP;
1253 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request)
1255 ldap_instance_t *inst = instance;
1257 if (inst->postauth) {
1258 return user_modify(inst, request, inst->postauth);
1261 return RLM_MODULE_NOOP;
1265 /* globally exported name */
1266 module_t rlm_ldap = {
1269 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1270 sizeof(ldap_instance_t),
1272 mod_instantiate, /* instantiation */
1273 mod_detach, /* detach */
1275 mod_authenticate, /* authentication */
1276 mod_authorize, /* authorization */
1277 NULL, /* preaccounting */
1278 mod_accounting, /* accounting */
1279 NULL, /* checksimul */
1280 NULL, /* pre-proxy */
1281 NULL, /* post-proxy */
1282 mod_post_auth /* post-auth */