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.
29 #include <freeradius-devel/ident.h>
32 #include <freeradius-devel/rad_assert.h>
42 const FR_NAME_NUMBER ldap_scope[] = {
43 { "sub", LDAP_SCOPE_SUB },
44 { "one", LDAP_SCOPE_ONE },
45 { "base", LDAP_SCOPE_BASE },
53 static CONF_PARSER tls_config[] = {
54 {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, start_tls), NULL, "no"},
55 {"cacertfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertfile), NULL, NULL},
56 {"cacertdir", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertdir), NULL, NULL},
57 {"certfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_certfile), NULL, NULL},
58 {"keyfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_keyfile), NULL, NULL}, // OK if it changes on HUP
59 {"randfile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_randfile), NULL, NULL},
60 {"require_cert",PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
62 { NULL, -1, 0, NULL, NULL }
66 static CONF_PARSER profile_config[] = {
67 {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, profile_attr), NULL, NULL},
68 {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, default_profile), NULL, NULL},
69 {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, profile_filter), NULL, NULL},
71 { NULL, -1, 0, NULL, NULL }
77 static CONF_PARSER user_config[] = {
78 {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_filter), NULL, "(uid=%u)"},
79 {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_scope_str), NULL, "sub"},
80 {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_base_dn), NULL, NULL},
82 {"access_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_access_attr), NULL, NULL},
83 {"access_positive", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, access_positive), NULL, "yes"},
85 { NULL, -1, 0, NULL, NULL }
91 static CONF_PARSER group_config[] = {
92 {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_filter), NULL, NULL},
93 {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_scope_str), NULL, "sub"},
94 {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_base_dn), NULL, NULL},
96 {"name_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_name_attr), NULL, "cn"},
97 {"membership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_membership_attr), NULL, NULL},
98 {"membership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_membership_filter), NULL, NULL},
99 {"cacheable_name", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_name), NULL, "no"},
100 {"cacheable_dn", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_dn), NULL, "no"},
102 { NULL, -1, 0, NULL, NULL }
106 * Reference for accounting updates
108 static const CONF_PARSER acct_section_config[] = {
109 {"reference", PW_TYPE_STRING_PTR, offsetof(ldap_acct_section_t, reference), NULL, "."},
111 {NULL, -1, 0, NULL, NULL}
115 * Various options that don't belong in the main configuration.
117 * Note that these overlap a bit with the connection pool code!
119 static CONF_PARSER option_config[] = {
121 * Debugging flags to the server
123 {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance_t,ldap_debug), NULL, "0x0000"},
125 {"chase_referrals", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,chase_referrals), NULL, NULL},
127 {"rebind", PW_TYPE_BOOLEAN,offsetof(ldap_instance_t,rebind), NULL, NULL},
129 /* timeout on network activity */
130 {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,net_timeout), NULL, "10"},
132 /* timeout for search results */
133 {"res_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,res_timeout), NULL, "20"},
135 /* allow server unlimited time for search (server-side limit) */
136 {"srv_timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance_t,srv_timelimit), NULL, "20"},
138 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
139 {"idle", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_idle), NULL, "60"},
141 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
142 {"probes", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_probes), NULL, "3"},
144 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
145 {"interval", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_interval), NULL, "30"},
148 { NULL, -1, 0, NULL, NULL }
152 static const CONF_PARSER module_config[] = {
153 {"server", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,server), NULL, "localhost"},
154 {"port", PW_TYPE_INTEGER, offsetof(ldap_instance_t,port), NULL, "389"},
156 {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,password), NULL, ""},
157 {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,admin_dn), NULL, ""},
159 {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,base_dn), NULL, ""},
162 /* support for eDirectory Universal Password */
163 {"edir", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir), NULL, NULL}, /* NULL defaults to "no" */
166 * Attempt to bind with the Cleartext password we got from eDirectory
167 * Universal password for additional authorization checks.
169 {"edir_autz", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
172 { "user", PW_TYPE_SUBSECTION, 0, NULL, (const void *) user_config },
174 { "group", PW_TYPE_SUBSECTION, 0, NULL, (const void *) group_config },
176 { "profiles", PW_TYPE_SUBSECTION, 0, NULL, (const void *) profile_config },
178 { "options", PW_TYPE_SUBSECTION, 0, NULL, (const void *) option_config },
180 { "tls", PW_TYPE_SUBSECTION, 0, NULL, (const void *) tls_config },
182 {NULL, -1, 0, NULL, NULL}
185 /** Expand an LDAP URL into a query, and return a string result from that query.
188 static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
189 char *out, size_t freespace)
193 ldap_instance_t *inst = instance;
194 LDAPURLDesc *ldap_url;
195 LDAPMessage *result = NULL;
196 LDAPMessage *entry = NULL;
202 char buffer[LDAP_MAX_DN_STR_LEN + LDAP_MAX_FILTER_STR_LEN];
204 if (strchr(fmt, '%') != NULL) {
205 if (!radius_xlat(buffer, sizeof(buffer), fmt, request, rlm_ldap_escape_func, NULL)) {
206 RDEBUGE("Unable to create LDAP URL");
214 if (!ldap_is_ldap_url(url)) {
215 RDEBUGE("String passed does not look like an LDAP URL");
219 if (ldap_url_parse(url, &ldap_url)){
220 RDEBUGE("Parsing LDAP URL failed");
225 * Nothing, empty string, "*" string, or got 2 things, die.
227 if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
228 !*ldap_url->lud_attrs[0] ||
229 (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
230 ldap_url->lud_attrs[1]) {
231 RDEBUGE("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
236 if (ldap_url->lud_host &&
237 ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
238 (ldap_url->lud_port != inst->port))) {
239 RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
244 conn = rlm_ldap_get_socket(inst, request);
245 if (!conn) goto free_urldesc;
247 memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
249 status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
252 case LDAP_PROC_SUCCESS:
254 case LDAP_PROC_NO_RESULT:
255 RDEBUG("Search returned not found");
263 entry = ldap_first_entry(conn->handle, result);
265 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
266 RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
270 vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
272 RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
276 length = strlen(vals[0]);
277 if (length >= freespace){
282 strlcpy(out, vals[0], freespace);
285 ldap_value_free(vals);
287 ldap_msgfree(result);
289 rlm_ldap_release_socket(inst, conn);
291 ldap_free_urldesc(ldap_url);
296 /** Perform LDAP-Group comparison checking
298 * Attempts to match users to groups using a variety of methods.
300 * @param instance of the rlm_ldap module.
301 * @param request Current request.
302 * @param thing Unknown.
303 * @param check Which group to check for user membership.
304 * @param check_pairs Unknown.
305 * @param reply_pairs Unknown.
306 * @return 1 on failure (or if the user is not a member), else 0.
308 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
309 UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
311 ldap_instance_t *inst = instance;
317 ldap_handle_t *conn = NULL;
320 RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
322 if (check->length == 0) {
323 RDEBUG("Cannot do comparison (group name is empty)");
328 * Check if we can do cached membership verification
330 check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
331 if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
332 switch(rlm_ldap_check_cached(inst, request, check)) {
333 case RLM_MODULE_NOTFOUND:
342 conn = rlm_ldap_get_socket(inst, request);
346 * This is used in the default membership filter.
348 user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, FALSE, NULL, &rcode);
350 rlm_ldap_release_socket(inst, conn);
357 * Check groupobj user membership
359 if (inst->groupobj_membership_filter) {
360 switch(rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) {
361 case RLM_MODULE_NOTFOUND:
373 * Check userobj group membership
375 if (inst->userobj_membership_attr) {
376 switch(rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) {
377 case RLM_MODULE_NOTFOUND:
390 rlm_ldap_release_socket(inst, conn);
394 RDEBUG("User is not a member of specified group");
402 /** Detach from the LDAP server and cleanup internal state.
405 static int mod_detach(void *instance)
407 ldap_instance_t *inst = instance;
409 fr_connection_pool_delete(inst->pool);
411 if (inst->user_map) {
412 radius_mapfree(&inst->user_map);
418 /** Parse an accounting sub section.
420 * Allocate a new ldap_acct_section_t and write the config data into it.
422 * @param[in] inst rlm_ldap configuration.
423 * @param[in] parent of the config section.
424 * @param[out] config to write the sub section parameters to.
425 * @param[in] comp The section name were parsing the config for.
426 * @return 0 on success, else < 0 on failure.
428 static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
429 rlm_components_t comp)
433 const char *name = section_type_value[comp].section;
435 cs = cf_section_sub_find(parent, name);
437 radlog(L_INFO, "rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
438 "from this section", inst->xlat_name, name);
443 *config = talloc_zero(inst, ldap_acct_section_t);
444 if (cf_section_parse(cs, *config, acct_section_config) < 0) {
445 LDAP_ERR("Failed parsing configuration for section %s", name);
455 /** Instantiate the module
457 * Creates a new instance of the module reading parameters from a configuration section.
459 * @param conf to parse.
460 * @param instance Where to write pointer to configuration data.
461 * @return 0 on success < 0 on failure.
463 static int mod_instantiate(CONF_SECTION *conf, void **instance)
465 ldap_instance_t *inst;
467 *instance = inst = talloc_zero(conf, ldap_instance_t);
468 if (!inst) return -1;
472 inst->chase_referrals = 2; /* use OpenLDAP defaults */
475 inst->xlat_name = cf_section_name2(conf);
476 if (!inst->xlat_name) {
477 inst->xlat_name = cf_section_name1(conf);
481 * If the configuration parameters can't be parsed, then fail.
483 if ((cf_section_parse(conf, inst, module_config) < 0) ||
484 (parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
485 (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
486 LDAP_ERR("Failed parsing configuration");
492 LDAP_ERR("Missing 'server' directive");
498 * Sanity checks for cacheable groups code.
500 if (inst->cacheable_group_name && inst->groupobj_membership_filter && !inst->groupobj_name_attr) {
501 LDAP_ERR("Directive 'group.name_attribute' must be set if cacheable group names are enabled");
507 * Copy across values from base_dn to the object specific base_dn.
509 if (!inst->groupobj_base_dn) {
510 if (!inst->base_dn) {
511 LDAP_ERR("Missing 'base_dn' directive");
516 inst->groupobj_base_dn = inst->base_dn;
519 if (!inst->userobj_base_dn) {
520 if (!inst->base_dn) {
521 LDAP_ERR("Missing 'base_dn' directive");
526 inst->userobj_base_dn = inst->base_dn;
530 * Check for URLs. If they're used and the library doesn't support them, then complain.
533 if (ldap_is_ldap_url(inst->server)) {
534 #ifdef HAVE_LDAP_INITIALIZE
538 LDAP_ERR("'server' directive is in URL form but ldap_initialize() is not available");
544 * Workaround for servers which support LDAPS but not START TLS
546 if (inst->port == LDAPS_PORT || inst->tls_mode) {
547 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
552 #if LDAP_SET_REBIND_PROC_ARGS != 3
554 * The 2-argument rebind doesn't take an instance variable. Our rebind function needs the instance
555 * variable for the username, password, etc.
557 if (inst->rebind == 1) {
558 LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API "
566 * Convert scope strings to integers
568 inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1);
569 if (inst->userobj_scope < 0) {
570 LDAP_ERR("Invalid 'user.scope' value \"%s\", expected 'sub', 'one' or 'base'",
571 inst->userobj_scope_str);
575 inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1);
576 if (inst->groupobj_scope < 0) {
577 LDAP_ERR("Invalid 'group.scope' value \"%s\", expected 'sub', 'one' or 'base'",
578 inst->groupobj_scope_str);
583 * Build the attribute map
585 if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
590 * Group comparison checks.
592 inst->group_da = dict_attrbyvalue(PW_LDAP_GROUP, 0);
593 paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, rlm_ldap_groupcmp, inst);
594 if (cf_section_name2(conf)) {
598 snprintf(buffer, sizeof(buffer), "%s-Ldap-Group",
600 memset(&flags, 0, sizeof(flags));
602 dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
603 inst->group_da = dict_attrbyname(buffer);
604 if (!inst->group_da) {
605 LDAP_ERR("Failed creating attribute %s", buffer);
611 paircompare_register(inst->group_da->attr, PW_USER_NAME, rlm_ldap_groupcmp, inst);
614 xlat_register(inst->xlat_name, ldap_xlat, inst);
617 * Initialize the socket pool.
619 inst->pool = fr_connection_pool_init(inst->cs, inst, rlm_ldap_conn_create, NULL, rlm_ldap_conn_delete);
632 /** Check the user's password against ldap directory
634 * @param instance rlm_ldap configuration.
635 * @param request Current request.
636 * @return one of the RLM_MODULE_* values.
638 static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
643 ldap_instance_t *inst = instance;
647 * Ensure that we're being passed a plain-text password, and not
651 if (!request->username) {
652 RDEBUGE("Attribute \"User-Name\" is required for authentication");
654 return RLM_MODULE_INVALID;
657 if (!request->password ||
658 (request->password->da->attr != PW_USER_PASSWORD)) {
659 RDEBUGW("You have set \"Auth-Type := LDAP\" somewhere.");
660 RDEBUGW("*********************************************");
661 RDEBUGW("* THAT CONFIGURATION IS WRONG. DELETE IT. ");
662 RDEBUGW("* YOU ARE PREVENTING THE SERVER FROM WORKING.");
663 RDEBUGW("*********************************************");
665 RDEBUGE("Attribute \"User-Password\" is required for authentication.");
667 return RLM_MODULE_INVALID;
670 if (request->password->length == 0) {
671 RDEBUGE("Empty password supplied");
673 return RLM_MODULE_INVALID;
676 RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
678 conn = rlm_ldap_get_socket(inst, request);
679 if (!conn) return RLM_MODULE_FAIL;
682 * Get the DN by doing a search.
684 dn = rlm_ldap_find_user(inst, request, &conn, NULL, FALSE, NULL, &rcode);
686 rlm_ldap_release_socket(inst, conn);
694 conn->rebound = TRUE;
695 status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, TRUE);
697 case LDAP_PROC_SUCCESS:
698 rcode = RLM_MODULE_OK;
699 RDEBUG("Bind as user \"%s\" was successful", dn);
702 case LDAP_PROC_NOT_PERMITTED:
703 rcode = RLM_MODULE_USERLOCK;
706 case LDAP_PROC_REJECT:
707 rcode = RLM_MODULE_REJECT;
710 case LDAP_PROC_BAD_DN:
711 rcode = RLM_MODULE_INVALID;
714 case LDAP_PROC_NO_RESULT:
715 rcode = RLM_MODULE_NOTFOUND;
719 rcode = RLM_MODULE_FAIL;
723 rlm_ldap_release_socket(inst, conn);
728 /** Check if user is authorized for remote access
731 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
733 rlm_rcode_t rcode = RLM_MODULE_OK;
737 ldap_instance_t *inst = instance;
741 LDAPMessage *result, *entry;
742 const char *dn = NULL;
743 rlm_ldap_map_xlat_t expanded; /* faster that mallocing every time */
745 if (!request->username) {
746 RDEBUG2("Attribute \"User-Name\" is required for authorization.");
748 return RLM_MODULE_NOOP;
752 * Check for valid input, zero length names not permitted
754 if (request->username->length == 0) {
755 RDEBUG2("Zero length username not permitted");
757 return RLM_MODULE_INVALID;
760 if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
761 return RLM_MODULE_FAIL;
764 conn = rlm_ldap_get_socket(inst, request);
765 if (!conn) return RLM_MODULE_FAIL;
768 * Add any additional attributes we need for checking access, memberships, and profiles
770 if (inst->userobj_access_attr) {
771 expanded.attrs[expanded.count++] = inst->userobj_access_attr;
774 if (inst->userobj_membership_attr) {
775 expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
778 if (inst->profile_attr) {
779 expanded.attrs[expanded.count++] = inst->profile_attr;
782 expanded.attrs[expanded.count] = NULL;
784 dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, TRUE, &result, &rcode);
789 entry = ldap_first_entry(conn->handle, result);
791 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
792 RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
800 if (inst->userobj_access_attr) {
801 rcode = rlm_ldap_check_access(inst, request, conn, entry);
802 if (rcode != RLM_MODULE_OK) {
808 * Check if we need to cache group memberships
810 if (inst->cacheable_group_dn || inst->cacheable_group_name) {
811 rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry);
812 if (rcode != RLM_MODULE_OK) {
816 rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
817 if (rcode != RLM_MODULE_OK) {
824 * We already have a Cleartext-Password. Skip edir.
826 if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
831 * Retrieve Universal Password if we use eDirectory
836 size_t pass_size = sizeof(password);
839 * Retrive universal password
841 res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
843 RDEBUGW("Failed to retrieve eDirectory password");
844 rcode = RLM_MODULE_NOOP;
850 * Add Cleartext-Password attribute to the request
852 vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
853 strlcpy(vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
854 vp->length = pass_size;
856 RDEBUG2("Added eDirectory password in check items as %s = %s", vp->da->name, vp->vp_strvalue);
858 if (inst->edir_autz) {
859 RDEBUG2("Binding as user for eDirectory authorization checks");
863 conn->rebound = TRUE;
864 status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, TRUE);
866 case LDAP_PROC_SUCCESS:
867 rcode = RLM_MODULE_OK;
868 RDEBUG("Bind as user \"%s\" was successful", dn);
871 case LDAP_PROC_NOT_PERMITTED:
872 rcode = RLM_MODULE_USERLOCK;
875 case LDAP_PROC_REJECT:
876 rcode = RLM_MODULE_REJECT;
879 case LDAP_PROC_BAD_DN:
880 rcode = RLM_MODULE_INVALID;
883 case LDAP_PROC_NO_RESULT:
884 rcode = RLM_MODULE_NOTFOUND;
888 rcode = RLM_MODULE_FAIL;
899 * Apply ONE user profile, or a default user profile.
901 vp = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
902 if (vp || inst->default_profile) {
903 const char *profile = inst->default_profile;
905 if (vp) profile = vp->vp_strvalue;
907 rlm_ldap_map_profile(inst, request, &conn, profile, &expanded);
911 * Apply a SET of user profiles.
913 if (inst->profile_attr) {
914 vals = ldap_get_values(conn->handle, entry, inst->profile_attr);
916 for (i = 0; vals[i] != NULL; i++) {
917 rlm_ldap_map_profile(inst, request, &conn, vals[i], &expanded);
920 ldap_value_free(vals);
924 if (inst->user_map) {
925 rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
926 rlm_ldap_check_reply(inst, request);
930 rlm_ldap_map_xlat_free(&expanded);
932 ldap_msgfree(result);
934 rlm_ldap_release_socket(inst, conn);
939 /** Modify user's object in LDAP
941 * Process a modifcation map to update a user object in the LDAP directory.
943 * @param inst rlm_ldap instance.
944 * @param request Current request.
945 * @param section that holds the map to process.
946 * @return one of the RLM_MODULE_* values.
948 static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
950 rlm_rcode_t rcode = RLM_MODULE_OK;
952 ldap_handle_t *conn = NULL;
954 LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
955 LDAPMod **modify = mod_p;
957 char *passed[LDAP_MAX_ATTRMAP * 2];
958 int i, total = 0, last_pass = 0;
960 char *expanded[LDAP_MAX_ATTRMAP];
968 * Build our set of modifications using the update sections in
975 char path[MAX_STRING_LEN];
982 * Locate the update section were going to be using
984 if (section->reference[0] != '.') {
988 if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1, section->reference, request, NULL, NULL)) {
992 ci = cf_reference_item(NULL, section->cs, path);
997 if (!cf_item_is_section(ci)){
998 RDEBUGE("Reference must resolve to a section");
1003 cs = cf_section_sub_find(cf_itemtosection(ci), "update");
1005 RDEBUGE("Section must contain 'update' subsection");
1011 * Iterate over all the pairs, building our mods array
1013 for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
1014 int do_xlat = FALSE;
1016 if (total == LDAP_MAX_ATTRMAP) {
1017 RDEBUGE("Modify map size exceeded");
1022 if (!cf_item_is_pair(ci)) {
1023 RDEBUGE("Entry is not in \"ldap-attribute = value\" format");
1029 * Retrieve all the information we need about the pair
1031 cp = cf_itemtopair(ci);
1032 value = cf_pair_value(cp);
1033 attr = cf_pair_attr(cp);
1034 op = cf_pair_operator(cp);
1036 if (!value || (*value == '\0')) {
1037 RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
1042 switch (cf_pair_value_type(cp))
1045 case T_SINGLE_QUOTED_STRING:
1047 case T_BACK_QUOTED_STRING:
1048 case T_DOUBLE_QUOTED_STRING:
1056 if (op == T_OP_CMP_FALSE) {
1057 passed[last_pass] = NULL;
1058 } else if (do_xlat) {
1059 p = rad_malloc(1024);
1060 if (radius_xlat(p, 1024, value, request, NULL, NULL) <= 0) {
1061 RDEBUG("xlat failed or empty value string, skipping attribute \"%s\"", attr);
1068 expanded[last_exp++] = p;
1069 passed[last_pass] = p;
1074 memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
1077 passed[last_pass + 1] = NULL;
1079 mod_s[total].mod_values = &(passed[last_pass]);
1086 * T_OP_EQ is *NOT* supported, it is impossible to
1087 * support because of the lack of transactions in LDAP
1090 mod_s[total].mod_op = LDAP_MOD_ADD;
1094 mod_s[total].mod_op = LDAP_MOD_REPLACE;
1098 case T_OP_CMP_FALSE:
1099 mod_s[total].mod_op = LDAP_MOD_DELETE;
1102 #ifdef LDAP_MOD_INCREMENT
1104 mod_s[total].mod_op = LDAP_MOD_INCREMENT;
1108 RDEBUGE("Operator '%s' is not supported for LDAP modify operations",
1109 fr_int2str(fr_tokens, op, "¿unknown?"));
1115 * Now we know the value is ok, copy the pointers into
1116 * the ldapmod struct.
1118 memcpy(&(mod_s[total].mod_type), &(attr), sizeof(mod_s[total].mod_type));
1120 mod_p[total] = &(mod_s[total]);
1125 rcode = RLM_MODULE_NOOP;
1129 mod_p[total] = NULL;
1131 conn = rlm_ldap_get_socket(inst, request);
1132 if (!conn) return RLM_MODULE_FAIL;
1135 dn = rlm_ldap_find_user(inst, request, &conn, NULL, FALSE, NULL, &rcode);
1136 if (!dn || (rcode != RLM_MODULE_OK)) {
1140 rcode = rlm_ldap_modify(inst, request, &conn, dn, modify);
1145 * Free up any buffers we allocated for xlat expansion
1147 for (i = 0; i < last_exp; i++) {
1151 rlm_ldap_release_socket(inst, conn);
1156 static rlm_rcode_t mod_accounting(void *instance, REQUEST * request) {
1157 ldap_instance_t *inst = instance;
1159 if (inst->accounting) {
1160 return user_modify(inst, request, inst->accounting);
1163 return RLM_MODULE_NOOP;
1166 static rlm_rcode_t mod_post_auth(void *instance, REQUEST * request)
1168 ldap_instance_t *inst = instance;
1170 if (inst->postauth) {
1171 return user_modify(inst, request, inst->postauth);
1174 return RLM_MODULE_NOOP;
1178 /* globally exported name */
1179 module_t rlm_ldap = {
1182 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1183 mod_instantiate, /* instantiation */
1184 mod_detach, /* detach */
1186 mod_authenticate, /* authentication */
1187 mod_authorize, /* authorization */
1188 NULL, /* preaccounting */
1189 mod_accounting, /* accounting */
1190 NULL, /* checksimul */
1191 NULL, /* pre-proxy */
1192 NULL, /* post-proxy */
1193 mod_post_auth /* post-auth */