Merge pull request #1835 from qnet-herwin/retry_winbind_auth_with_normalized_username
authorMatthew Newton <matthew-git@newtoncomputing.co.uk>
Tue, 20 Dec 2016 13:08:31 +0000 (13:08 +0000)
committerGitHub <noreply@github.com>
Tue, 20 Dec 2016 13:08:31 +0000 (13:08 +0000)
Allow authentication retry in winbind

raddb/mods-available/mschap
src/modules/rlm_mschap/auth_wbclient.c
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_mschap/rlm_mschap.h

index 4673fa7..18f6005 100644 (file)
@@ -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
index a836195..f0bc166 100644 (file)
@@ -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
index 5a3a67a..e2d4878 100644 (file)
@@ -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 */
index 1ce1ad4..4109715 100644 (file)
@@ -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