static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; int ldap_errno; int i; ldap_instance_t *inst = instance; struct berval **values; VALUE_PAIR *vp; ldap_handle_t *conn; LDAPMessage *result, *entry; char const *dn = NULL; rlm_ldap_map_xlat_t expanded; /* faster than mallocing every time */ if (!request->username) { RDEBUG2("Attribute \"User-Name\" is required for authorization"); return RLM_MODULE_NOOP; } /* * Check for valid input, zero length names not permitted */ if (request->username->vp_length == 0) { RDEBUG2("Zero length username not permitted"); return RLM_MODULE_INVALID; } if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) { return RLM_MODULE_FAIL; } conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; /* * Add any additional attributes we need for checking access, memberships, and profiles */ if (inst->userobj_access_attr) { expanded.attrs[expanded.count++] = inst->userobj_access_attr; } if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) { expanded.attrs[expanded.count++] = inst->userobj_membership_attr; } if (inst->profile_attr) { expanded.attrs[expanded.count++] = inst->profile_attr; } if (inst->valuepair_attr) { expanded.attrs[expanded.count++] = inst->valuepair_attr; } expanded.attrs[expanded.count] = NULL; dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode); if (!dn) { goto finish; } entry = ldap_first_entry(conn->handle, result); if (!entry) { ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); goto finish; } /* * Check for access. */ if (inst->userobj_access_attr) { rcode = rlm_ldap_check_access(inst, request, conn, entry); if (rcode != RLM_MODULE_OK) { goto finish; } } /* * Check if we need to cache group memberships */ if (inst->cacheable_group_dn || inst->cacheable_group_name) { if (inst->userobj_membership_attr) { rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr); if (rcode != RLM_MODULE_OK) { goto finish; } } rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn); if (rcode != RLM_MODULE_OK) { goto finish; } } #ifdef WITH_EDIR /* * We already have a Cleartext-Password. Skip edir. */ if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) { goto skip_edir; } /* * Retrieve Universal Password if we use eDirectory */ if (inst->edir) { int res = 0; char password[256]; size_t pass_size = sizeof(password); /* * Retrive universal password */ res = nmasldap_get_password(conn->handle, dn, password, &pass_size); if (res != 0) { REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res)); rcode = RLM_MODULE_FAIL; goto finish; } /* * Add Cleartext-Password attribute to the request */ vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0); pairstrcpy(vp, password); vp->vp_length = pass_size; if (RDEBUG_ENABLED3) { RDEBUG3("Added eDirectory password. control:%s += '%s'", vp->da->name, vp->vp_strvalue); } else { RDEBUG2("Added eDirectory password"); } if (inst->edir_autz) { RDEBUG2("Binding as user for eDirectory authorization checks"); /* * Bind as the user */ conn->rebound = true; status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; RDEBUG("Bind as user '%s' was successful", dn); break; case LDAP_PROC_NOT_PERMITTED: rcode = RLM_MODULE_USERLOCK; goto finish; case LDAP_PROC_REJECT: rcode = RLM_MODULE_REJECT; goto finish; case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; goto finish; case LDAP_PROC_NO_RESULT: rcode = RLM_MODULE_NOTFOUND; goto finish; default: rcode = RLM_MODULE_FAIL; goto finish; }; } } skip_edir: #endif /* * Apply ONE user profile, or a default user profile. */ if (inst->default_profile) { char profile[1024]; if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) { REDEBUG("Failed creating default profile string"); rcode = RLM_MODULE_INVALID; goto finish; } rlm_ldap_map_profile(inst, request, &conn, profile, &expanded); } /* * Apply a SET of user profiles. */ if (inst->profile_attr) { values = ldap_get_values_len(conn->handle, entry, inst->profile_attr); if (values != NULL) { for (i = 0; values[i] != NULL; i++) { char *value; value = rlm_ldap_berval_to_string(request, values[i]); rlm_ldap_map_profile(inst, request, &conn, value, &expanded); talloc_free(value); } ldap_value_free_len(values); } } if (inst->user_map || inst->valuepair_attr) { RDEBUG("Processing user attributes"); RINDENT(); rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry); REXDENT(); rlm_ldap_check_reply(inst, request); } finish: rlm_ldap_map_xlat_free(&expanded); if (result) ldap_msgfree(result); mod_conn_release(inst, conn); return rcode; }
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request) { rlm_rcode_t rcode; ldap_rcode_t status; char const *dn; ldap_instance_t *inst = instance; ldap_handle_t *conn; /* * Ensure that we're being passed a plain-text password, and not * anything else. */ if (!request->username) { REDEBUG("Attribute \"User-Name\" is required for authentication"); return RLM_MODULE_INVALID; } if (!request->password || (request->password->da->attr != PW_USER_PASSWORD)) { RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere"); RWDEBUG("*********************************************"); RWDEBUG("* THAT CONFIGURATION IS WRONG. DELETE IT. "); RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING"); RWDEBUG("*********************************************"); REDEBUG("Attribute \"User-Password\" is required for authentication"); return RLM_MODULE_INVALID; } if (request->password->vp_length == 0) { REDEBUG("Empty password supplied"); return RLM_MODULE_INVALID; } RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue); conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; /* * Get the DN by doing a search. */ dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!dn) { mod_conn_release(inst, conn); return rcode; } /* * Bind as the user */ conn->rebound = true; status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, true); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; RDEBUG("Bind as user \"%s\" was successful", dn); break; case LDAP_PROC_NOT_PERMITTED: rcode = RLM_MODULE_USERLOCK; break; case LDAP_PROC_REJECT: rcode = RLM_MODULE_REJECT; break; case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; break; case LDAP_PROC_NO_RESULT: rcode = RLM_MODULE_NOTFOUND; break; default: rcode = RLM_MODULE_FAIL; break; }; mod_conn_release(inst, conn); return rcode; }
/** Modify user's object in LDAP * * Process a modifcation map to update a user object in the LDAP directory. * * @param inst rlm_ldap instance. * @param request Current request. * @param section that holds the map to process. * @return one of the RLM_MODULE_* values. */ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; ldap_handle_t *conn = NULL; LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP]; LDAPMod **modify = mod_p; char *passed[LDAP_MAX_ATTRMAP * 2]; int i, total = 0, last_pass = 0; char *expanded[LDAP_MAX_ATTRMAP]; int last_exp = 0; char const *attr; char const *value; char const *dn; /* * Build our set of modifications using the update sections in * the config. */ CONF_ITEM *ci; CONF_PAIR *cp; CONF_SECTION *cs; FR_TOKEN op; char path[MAX_STRING_LEN]; char *p = path; rad_assert(section); /* * Locate the update section were going to be using */ if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) { goto error; } ci = cf_reference_item(NULL, section->cs, path); if (!ci) { goto error; } if (!cf_item_is_section(ci)){ REDEBUG("Reference must resolve to a section"); goto error; } cs = cf_section_sub_find(cf_itemtosection(ci), "update"); if (!cs) { REDEBUG("Section must contain 'update' subsection"); goto error; } /* * Iterate over all the pairs, building our mods array */ for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { bool do_xlat = false; if (total == LDAP_MAX_ATTRMAP) { REDEBUG("Modify map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { REDEBUG("Entry is not in \"ldap-attribute = value\" format"); goto error; } /* * Retrieve all the information we need about the pair */ cp = cf_itemtopair(ci); value = cf_pair_value(cp); attr = cf_pair_attr(cp); op = cf_pair_operator(cp); if (!value || (*value == '\0')) { RDEBUG("Empty value string, skipping attribute \"%s\"", attr); continue; } switch (cf_pair_value_type(cp)) { case T_BARE_WORD: case T_SINGLE_QUOTED_STRING: break; case T_BACK_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: do_xlat = true; break; default: rad_assert(0); goto error; } if (op == T_OP_CMP_FALSE) { passed[last_pass] = NULL; } else if (do_xlat) { char *exp = NULL; if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) { RDEBUG("Skipping attribute \"%s\"", attr); talloc_free(exp); continue; } expanded[last_exp++] = exp; passed[last_pass] = exp; /* * Static strings */ } else { memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass])); } passed[last_pass + 1] = NULL; mod_s[total].mod_values = &(passed[last_pass]); last_pass += 2; switch (op) { /* * T_OP_EQ is *NOT* supported, it is impossible to * support because of the lack of transactions in LDAP */ case T_OP_ADD: mod_s[total].mod_op = LDAP_MOD_ADD; break; case T_OP_SET: mod_s[total].mod_op = LDAP_MOD_REPLACE; break; case T_OP_SUB: case T_OP_CMP_FALSE: mod_s[total].mod_op = LDAP_MOD_DELETE; break; #ifdef LDAP_MOD_INCREMENT case T_OP_INCRM: mod_s[total].mod_op = LDAP_MOD_INCREMENT; break; #endif default: REDEBUG("Operator '%s' is not supported for LDAP modify operations", fr_int2str(fr_tokens, op, "<INVALID>")); goto error; } /* * Now we know the value is ok, copy the pointers into * the ldapmod struct. */ memcpy(&(mod_s[total].mod_type), &attr, sizeof(mod_s[total].mod_type)); mod_p[total] = &(mod_s[total]); total++; } if (total == 0) { rcode = RLM_MODULE_NOOP; goto release; } mod_p[total] = NULL; conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!dn || (rcode != RLM_MODULE_OK)) { goto error; } status = rlm_ldap_modify(inst, request, &conn, dn, modify); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_REJECT: case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; break; default: rcode = RLM_MODULE_FAIL; break; }; release: error: /* * Free up any buffers we allocated for xlat expansion */ for (i = 0; i < last_exp; i++) { talloc_free(expanded[i]); } mod_conn_release(inst, conn); return rcode; }
/** Perform LDAP-Group comparison checking * * Attempts to match users to groups using a variety of methods. * * @param instance of the rlm_ldap module. * @param request Current request. * @param thing Unknown. * @param check Which group to check for user membership. * @param check_pairs Unknown. * @param reply_pairs Unknown. * @return 1 on failure (or if the user is not a member), else 0. */ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { ldap_instance_t *inst = instance; rlm_rcode_t rcode; bool found = false; bool check_is_dn; ldap_handle_t *conn = NULL; char const *user_dn; rad_assert(inst->groupobj_base_dn); RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue); if (check->vp_length == 0) { REDEBUG("Cannot do comparison (group name is empty)"); return 1; } /* * Check if we can do cached membership verification */ check_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length); if (check_is_dn) { char *norm; MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue))); rlm_ldap_normalise_dn(norm, check->vp_strvalue); pairstrsteal(check, norm); } if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) { switch (rlm_ldap_check_cached(inst, request, check)) { case RLM_MODULE_NOTFOUND: found = false; goto finish; case RLM_MODULE_OK: found = true; goto finish; /* * Fallback to dynamic search on failure */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: default: break; } } conn = mod_conn_get(inst, request); if (!conn) return 1; /* * This is used in the default membership filter. */ user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!user_dn) { mod_conn_release(inst, conn); return 1; } rad_assert(conn); /* * Check groupobj user membership */ if (inst->groupobj_membership_filter) { switch (rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); /* * Check userobj group membership */ if (inst->userobj_membership_attr) { switch (rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); finish: if (conn) mod_conn_release(inst, conn); if (!found) { RDEBUG("User is not a member of \"%s\"", check->vp_strvalue); return 1; } return 0; }
/** Perform LDAP-Group comparison checking * * Attempts to match users to groups using a variety of methods. * * @param instance of the rlm_ldap module. * @param request Current request. * @param thing Unknown. * @param check Which group to check for user membership. * @param check_pairs Unknown. * @param reply_pairs Unknown. * @return 1 on failure (or if the user is not a member), else 0. */ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { ldap_instance_t *inst = instance; rlm_rcode_t rcode; int found = false; int check_is_dn; ldap_handle_t *conn = NULL; char const *user_dn; if (!inst->groupobj_base_dn) { REDEBUG("Directive 'group.base_dn' must be set'"); return 1; } RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue); if (check->length == 0) { RDEBUG("Cannot do comparison (group name is empty)"); return 1; } /* * Check if we can do cached membership verification */ check_is_dn = rlm_ldap_is_dn(check->vp_strvalue); if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) { switch(rlm_ldap_check_cached(inst, request, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } conn = rlm_ldap_get_socket(inst, request); if (!conn) return 1; /* * This is used in the default membership filter. */ user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!user_dn) { rlm_ldap_release_socket(inst, conn); return 1; } rad_assert(conn); /* * Check groupobj user membership */ if (inst->groupobj_membership_filter) { switch(rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); /* * Check userobj group membership */ if (inst->userobj_membership_attr) { switch(rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); finish: if (conn) { rlm_ldap_release_socket(inst, conn); } if (!found) { RDEBUG("User is not a member of specified group"); return 1; } return 0; }