From: Herwin Weststrate Date: Wed, 9 Nov 2016 09:29:08 +0000 (+0100) Subject: Allow authentication retry in winbind X-Git-Tag: release_3_0_13~91^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F1835%2Fhead;p=thirdparty%2Ffreeradius-server.git Allow authentication retry in winbind A setup with the following properties: * Active Directory backend * FreeRadius with eap-inner-proxy * Windows client with single sign-on * User using different casing in username than in backend may result in failing connections. It looks like Windows reads the correct username from the domain server once it has logged in, and uses that to create the MS-CHAP2-Response attribute. The User-Name attribute is still the one with the incorrect casing, causing the authentication to fail. The introduced config option kicks in after a failed authentication: it reads the correct username from the backend, tries another authentication, and uses the found User-Name to calculate MS-CHAP2-Response if the second authentication works. --- diff --git a/raddb/mods-available/mschap b/raddb/mods-available/mschap index 4673fa7f9fd..18f600589b6 100644 --- a/raddb/mods-available/mschap +++ b/raddb/mods-available/mschap @@ -78,6 +78,15 @@ mschap { # winbind_username = "%{mschap:User-Name}" # winbind_domain = "%{mschap:NT-Domain}" + # When using single sign-on with a winbind connection and the + # client uses a different casing for the username than the + # casing is according to the backend, reauth may fail because + # of some Windows internals. This switch tries to find the + # user in the correct casing in the backend, and retry + # authentication with that username. + # +# winbind_retry_with_normalised_username = no + # # Information for the winbind connection pool. The configuration # items below are the same for all modules which use the new diff --git a/src/modules/rlm_mschap/auth_wbclient.c b/src/modules/rlm_mschap/auth_wbclient.c index a8361955bdf..f0bc1665bda 100644 --- a/src/modules/rlm_mschap/auth_wbclient.c +++ b/src/modules/rlm_mschap/auth_wbclient.c @@ -35,6 +35,42 @@ RCSID("$Id$") #define NT_LENGTH 24 +/** Use Winbind to normalise a username + * + * @param[in] tctx The talloc context where the result is parented from + * @param[in] ctx The winbind context + * @param[in] dom_name The domain of the user + * @param[in] name The username (without the domain) to be normalised + * @return The username with the casing according to the Winbind remote server, + * or NULL if the username could not be found. + */ +static char *wbclient_normalise_username(TALLOC_CTX *tctx, struct wbcContext *ctx, char const *dom_name, char const *name) +{ + struct wbcDomainSid sid; + enum wbcSidType name_type; + wbcErr err; + char *res_domain = NULL; + char *res_name = NULL; + char *res = NULL; + + /* Step 1: Convert a name to a sid */ + err = wbcCtxLookupName(ctx, dom_name, name, &sid, &name_type); + if (!WBC_ERROR_IS_OK(err)) + return NULL; + + /* Step 2: Convert the sid back to a name */ + err = wbcCtxLookupSid(ctx, &sid, &res_domain, &res_name, &name_type); + if (!WBC_ERROR_IS_OK(err)) + return NULL; + + MEM(res = talloc_strdup(tctx, res_name)); + + wbcFreeMemory(res_domain); + wbcFreeMemory(res_name); + + return res; +} + /* * Check NTLM authentication direct to winbind via * Samba's libwbclient library @@ -49,7 +85,7 @@ int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request, uint8_t nthashhash[NT_DIGEST_LENGTH]) { int rcode = -1; - struct wbcContext *wb_ctx; + struct wbcContext *wb_ctx = NULL; struct wbcAuthUserParams authparams; wbcErr err; int len; @@ -124,8 +160,45 @@ int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request, err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error); - fr_connection_release(inst->wb_pool, wb_ctx); + if (err == WBC_ERR_AUTH_ERROR && inst->wb_retry_with_normalised_username) { + VALUE_PAIR *vp_response, *vp_challenge; + char *normalised_username = wbclient_normalise_username(request, wb_ctx, authparams.domain_name, authparams.account_name); + if (normalised_username) { + RDEBUG2("Starting retry, normalised username %s to %s", authparams.account_name, normalised_username); + if (strcmp(authparams.account_name, normalised_username) != 0) { + authparams.account_name = normalised_username; + + /* Set PW_MS_CHAP_USER_NAME */ + if (!fr_pair_make(request->packet, &request->packet->vps, "MS-CHAP-User-Name", normalised_username, T_OP_SET)) { + RERROR("Failed creating MS-CHAP-User-Name"); + goto normalised_username_retry_failure; + } + + RDEBUG2("retrying authentication request user='%s' domain='%s'", authparams.account_name, + authparams.domain_name); + + /* Recalculate hash */ + if (!(vp_challenge = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY))) { + RERROR("Unable to get MS-CHAP-Challenge"); + goto normalised_username_retry_failure; + } + if (!(vp_response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY))) { + RERROR("Unable to get MS-CHAP2-Response"); + goto normalised_username_retry_failure; + } + mschap_challenge_hash(vp_response->vp_octets + 2, + vp_challenge->vp_octets, + normalised_username, + authparams.password.response.challenge); + err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error); + } +normalised_username_retry_failure: + talloc_free(normalised_username); + } + } + + fr_connection_release(inst->wb_pool, wb_ctx); /* * Try and give some useful feedback on what happened. There are only diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index 5a3a67ae7a6..e2d48783720 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -560,6 +560,7 @@ static const CONF_PARSER module_config[] = { { "retry_msg", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, retry_msg), NULL }, { "winbind_username", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_mschap_t, wb_username), NULL }, { "winbind_domain", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_mschap_t, wb_domain), NULL }, + { "winbind_retry_with_normalised_username", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, wb_retry_with_normalised_username), "no" }, #ifdef __APPLE__ { "use_open_directory", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, open_directory), "yes" }, #endif @@ -1971,6 +1972,17 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re mschap_result, mschap_version, smb_ctrl); if (rcode != RLM_MODULE_OK) return rcode; +#ifdef WITH_AUTH_WINBIND + if (inst->wb_retry_with_normalised_username) { + if ((response_name = fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_USER_NAME, 0, TAG_ANY))) { + if (strcmp(username_string, response_name->vp_strvalue)) { + RDEBUG2("Changing username %s to %s", username_string, response_name->vp_strvalue); + username_string = response_name->vp_strvalue; + } + } + } +#endif + mschap_auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ diff --git a/src/modules/rlm_mschap/rlm_mschap.h b/src/modules/rlm_mschap/rlm_mschap.h index 1ce1ad4a08c..4109715dd2f 100644 --- a/src/modules/rlm_mschap/rlm_mschap.h +++ b/src/modules/rlm_mschap/rlm_mschap.h @@ -39,6 +39,7 @@ typedef struct rlm_mschap_t { vp_tmpl_t *wb_username; vp_tmpl_t *wb_domain; fr_connection_pool_t *wb_pool; + bool wb_retry_with_normalised_username; #ifdef __APPLE__ bool open_directory; #endif