From: Nick Porter Date: Tue, 28 Mar 2023 13:51:27 +0000 (+0100) Subject: Rework mod_authorize_resume to use state, group and profile async lookups X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ecf026254f080805da1f0837d7bc55c7229fc8e;p=thirdparty%2Ffreeradius-server.git Rework mod_authorize_resume to use state, group and profile async lookups --- diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index 3514a47950..49e9a41bdb 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -1312,182 +1312,217 @@ static unlang_action_t mod_authorize_start(UNUSED rlm_rcode_t *p_result, UNUSED &autz_ctx->query); } +#define REPEAT_MOD_AUTHORIZE_RESUME \ + if (unlang_function_repeat_set(request, mod_authorize_resume) < 0) { \ + rcode = RLM_MODULE_FAIL; \ + goto finish; \ + } + +/** Resume function called after each potential yield in LDAP authorization + * + * Some operations may or may not yeild. E.g. if group membership is + * read from an attribute returned with the user object and is already + * in the correct form, that will not yeild. + * Hence, each state may fall through to the next. + * + * @param p_result Result of current authorization. + * @param priority Unused. + * @param request Current request. + * @param uctx Current authrorization context. + * @return One of the RLM_MODULE_* values. + */ static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx) { ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, ldap_autz_ctx_t); rlm_ldap_t const *inst = talloc_get_type_abort_const(autz_ctx->inst, rlm_ldap_t); - LDAPMessage *entry; - int i, ldap_errno; + ldap_autz_mod_env_t *mod_env = talloc_get_type_abort(autz_ctx->mod_env, ldap_autz_mod_env_t); + int ldap_errno; rlm_rcode_t rcode = RLM_MODULE_OK; - struct berval **values; LDAP *handle = fr_ldap_handle_thread_local(); - /* - * If a user entry has been found the current rcode will be OK - */ - if (*p_result != RLM_MODULE_OK) return UNLANG_ACTION_CALCULATE_RESULT; - - entry = ldap_first_entry(handle, autz_ctx->query->result); - if (!entry) { - ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno); - REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); + switch (autz_ctx->status) { + case LDAP_AUTZ_FIND: + /* + * If a user entry has been found the current rcode will be OK + */ + if (*p_result != RLM_MODULE_OK) return UNLANG_ACTION_CALCULATE_RESULT; - goto finish; - } + autz_ctx->entry = ldap_first_entry(handle, autz_ctx->query->result); + if (!autz_ctx->entry) { + ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno); + REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); - /* - * Check for access. - */ - if (inst->userobj_access_attr) { - rcode = rlm_ldap_check_access(inst, request, 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) { - rlm_ldap_cacheable_userobj(&rcode, inst, request, autz_ctx->ttrunk, entry, inst->userobj_membership_attr); + /* + * Check for access. + */ + if (inst->userobj_access_attr) { + rcode = rlm_ldap_check_access(inst, request, autz_ctx->entry); if (rcode != RLM_MODULE_OK) { goto finish; } } - rlm_ldap_cacheable_groupobj(&rcode, inst, request, autz_ctx->ttrunk); - if (rcode != RLM_MODULE_OK) { - goto finish; + /* + * Check if we need to cache group memberships + */ + if ((inst->cacheable_group_dn || inst->cacheable_group_name) && (inst->userobj_membership_attr)) { + REPEAT_MOD_AUTHORIZE_RESUME; + if (rlm_ldap_cacheable_userobj(&rcode, request, autz_ctx, + inst->userobj_membership_attr) == UNLANG_ACTION_PUSHED_CHILD) { + autz_ctx->status = LDAP_AUTZ_GROUP; + return UNLANG_ACTION_PUSHED_CHILD; + } + if (rcode != RLM_MODULE_OK) goto finish; } - } + FALL_THROUGH; + + case LDAP_AUTZ_GROUP: + if (inst->cacheable_group_dn || inst->cacheable_group_name) { + REPEAT_MOD_AUTHORIZE_RESUME; + if (rlm_ldap_cacheable_groupobj(&rcode, request, autz_ctx) == UNLANG_ACTION_PUSHED_CHILD) { + autz_ctx->status = LDAP_AUTZ_POST_GROUP; + return UNLANG_ACTION_PUSHED_CHILD; + } + if (rcode != RLM_MODULE_OK) goto finish; + } + FALL_THROUGH; + case LDAP_AUTZ_POST_GROUP: #ifdef WITH_EDIR - /* - * We already have a Password.Cleartext. Skip edir. - */ - if (fr_pair_find_by_da(&request->control_pairs, NULL, attr_cleartext_password)) goto skip_edir; - - /* - * Retrieve Universal Password if we use eDirectory - */ - if (inst->edir) { - fr_pair_t *vp; - int res = 0; - char password[256]; - size_t pass_size = sizeof(password); - char const *dn = rlm_find_user_dn_cached(request);; - /* - * Retrive universal password + * We already have a Password.Cleartext. Skip edir. */ - res = fr_ldap_edir_get_password(handle, dn, password, &pass_size); - if (res != 0) { - REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, fr_ldap_edir_errstr(res)); - rcode = RLM_MODULE_FAIL; - - goto finish; - } + if (fr_pair_find_by_da(&request->control_pairs, NULL, attr_cleartext_password)) goto skip_edir; /* - * Add Password.Cleartext attribute to the request + * Retrieve Universal Password if we use eDirectory */ - MEM(pair_update_control(&vp, attr_cleartext_password) >= 0); - fr_pair_value_bstrndup(vp, password, pass_size, true); - - if (RDEBUG_ENABLED3) { - RDEBUG3("Added eDirectory password. control.%pP", vp); - } else { - RDEBUG2("Added eDirectory password"); - } + if (inst->edir) { + fr_pair_t *vp; + int res = 0; + char password[256]; + size_t pass_size = sizeof(password); + char const *dn = rlm_find_user_dn_cached(request);; - if (inst->edir_autz) { - fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, - fr_ldap_thread_t); - - RDEBUG2("Binding as user for eDirectory authorization checks"); /* - * Bind as the user + * Retrive universal password */ - if (fr_ldap_bind_auth_async(request, thread, dn, vp->vp_strvalue) < 0) { + res = fr_ldap_edir_get_password(handle, dn, password, &pass_size); + if (res != 0) { + REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, fr_ldap_edir_errstr(res)); rcode = RLM_MODULE_FAIL; + goto finish; } - rcode = unlang_interpret_synchronous(unlang_interpret_event_list(request), request); + /* + * Add Password.Cleartext attribute to the request + */ + MEM(pair_update_control(&vp, attr_cleartext_password) >= 0); + fr_pair_value_bstrndup(vp, password, pass_size, true); - if (rcode != RLM_MODULE_OK) goto finish; + if (RDEBUG_ENABLED3) { + RDEBUG3("Added eDirectory password. control.%pP", vp); + } else { + RDEBUG2("Added eDirectory password"); + } + + if (inst->edir_autz) { + fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, + fr_ldap_thread_t); + + RDEBUG2("Binding as user for eDirectory authorization checks"); + /* + * Bind as the user + */ + if (fr_ldap_bind_auth_async(request, thread, dn, vp->vp_strvalue) < 0) { + rcode = RLM_MODULE_FAIL; + goto finish; + } + + rcode = unlang_interpret_synchronous(unlang_interpret_event_list(request), request); + + if (rcode != RLM_MODULE_OK) goto finish; + } } - } + FALL_THROUGH; -skip_edir: + case LDAP_AUTZ_POST_EDIR: + skip_edir: #endif + /* + * Apply ONE user profile, or a default user profile. + */ + if (mod_env->default_profile.type == FR_TYPE_STRING) { + unlang_action_t ret; + + REPEAT_MOD_AUTHORIZE_RESUME; + ret = rlm_ldap_map_profile(request, autz_ctx, mod_env->default_profile.vb_strvalue, + &autz_ctx->expanded); + switch (ret) { + case UNLANG_ACTION_FAIL: + rcode = RLM_MODULE_FAIL; + goto finish; - /* - * Apply ONE user profile, or a default user profile. - */ - if (inst->default_profile) { - char const *profile; - char profile_buff[1024]; - rlm_rcode_t ret; + case UNLANG_ACTION_PUSHED_CHILD: + autz_ctx->status = LDAP_AUTZ_POST_DEFAULT_PROFILE; + return UNLANG_ACTION_PUSHED_CHILD; - if (tmpl_expand(&profile, profile_buff, sizeof(profile_buff), - request, inst->default_profile, NULL, NULL) < 0) { - REDEBUG("Failed creating default profile string"); + default: + break; + } + } + FALL_THROUGH; - rcode = RLM_MODULE_INVALID; - goto finish; + case LDAP_AUTZ_POST_DEFAULT_PROFILE: + /* + * Apply a SET of user profiles. + */ + if (inst->profile_attr) { + autz_ctx->profile_values = ldap_get_values_len(handle, autz_ctx->entry, inst->profile_attr); } + FALL_THROUGH; - rlm_ldap_map_profile(&ret, inst, request, autz_ctx->ttrunk, profile, &autz_ctx->expanded); - switch (ret) { - case RLM_MODULE_INVALID: - rcode = RLM_MODULE_INVALID; - goto finish; + case LDAP_AUTZ_USER_PROFILE: + /* + * After each profile has been applied, execution will restart here. + * Start by clearing the previously used value. + */ + TALLOC_FREE(autz_ctx->profile_value); - case RLM_MODULE_FAIL: - rcode = RLM_MODULE_FAIL; - goto finish; + if (autz_ctx->profile_values && autz_ctx->profile_values[autz_ctx->value_idx]) { + unlang_action_t ret; - case RLM_MODULE_UPDATED: - rcode = RLM_MODULE_UPDATED; - FALL_THROUGH; - default: - break; - } - } + autz_ctx->profile_value = fr_ldap_berval_to_string(autz_ctx, autz_ctx->profile_values[autz_ctx->value_idx++]); + REPEAT_MOD_AUTHORIZE_RESUME; + ret = rlm_ldap_map_profile(request, autz_ctx, autz_ctx->profile_value, &autz_ctx->expanded); + switch (ret) { + case UNLANG_ACTION_FAIL: + rcode = RLM_MODULE_FAIL; + goto finish; - /* - * Apply a SET of user profiles. - */ - if (inst->profile_attr) { - values = ldap_get_values_len(handle, entry, inst->profile_attr); - if (values != NULL) { - for (i = 0; values[i] != NULL; i++) { - rlm_rcode_t ret; - char *value; - - value = fr_ldap_berval_to_string(request, values[i]); - rlm_ldap_map_profile(&ret, inst, request, autz_ctx->ttrunk, value, &autz_ctx->expanded); - talloc_free(value); - if (ret == RLM_MODULE_FAIL) { - ldap_value_free_len(values); - rcode = ret; - goto finish; - } + case UNLANG_ACTION_PUSHED_CHILD: + autz_ctx->status = LDAP_AUTZ_USER_PROFILE; + return UNLANG_ACTION_PUSHED_CHILD; + default: + break; } - ldap_value_free_len(values); } - } - - if (!map_list_empty(&inst->user_map) || inst->valuepair_attr) { - RDEBUG2("Processing user attributes"); - RINDENT(); - if (fr_ldap_map_do(request, inst->valuepair_attr, - &autz_ctx->expanded, entry) > 0) rcode = RLM_MODULE_UPDATED; - REXDENT(); - rlm_ldap_check_reply(&(module_ctx_t){.inst = autz_ctx->dlinst}, request, autz_ctx->ttrunk); + FALL_THROUGH; + + case LDAP_AUTZ_MAP: + if (!map_list_empty(&inst->user_map) || inst->valuepair_attr) { + RDEBUG2("Processing user attributes"); + RINDENT(); + if (fr_ldap_map_do(request, inst->valuepair_attr, + &autz_ctx->expanded, autz_ctx->entry) > 0) rcode = RLM_MODULE_UPDATED; + REXDENT(); + rlm_ldap_check_reply(&(module_ctx_t){.inst = autz_ctx->dlinst}, request, autz_ctx->ttrunk); + } } finish: