diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /src/modules/rlm_mschap/auth_wbclient.c | |
parent | Initial commit. (diff) | |
download | freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.tar.xz freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.zip |
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/modules/rlm_mschap/auth_wbclient.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/modules/rlm_mschap/auth_wbclient.c b/src/modules/rlm_mschap/auth_wbclient.c new file mode 100644 index 0000000..8b4a3ee --- /dev/null +++ b/src/modules/rlm_mschap/auth_wbclient.c @@ -0,0 +1,270 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file auth_wbclient.c + * @brief NTLM authentication against the wbclient library + * + * @copyright 2015 Matthew Newton + */ + +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#include <core/ntstatus.h> + +#include "rlm_mschap.h" +#include "mschap.h" +#include "auth_wbclient.h" + +#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 + * + * Returns: + * 0 success + * -1 auth failure + * -2 failed connecting to AD + * -648 password expired + */ +int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request, + uint8_t const *challenge, uint8_t const *response, + uint8_t nthashhash[NT_DIGEST_LENGTH]) +{ + int rcode = -1; + struct wbcContext *wb_ctx = NULL; + struct wbcAuthUserParams authparams; + wbcErr err; + int len; + struct wbcAuthUserInfo *info = NULL; + struct wbcAuthErrorInfo *error = NULL; + char user_name_buf[500]; + char domain_name_buf[500]; + uint8_t resp[NT_LENGTH]; + + /* + * Clear the auth parameters - this is important, as + * there are options that will cause wbcAuthenticateUserEx + * to bomb out if not zero. + */ + memset(&authparams, 0, sizeof(authparams)); + + /* + * wb_username must be set for this function to be called + */ + rad_assert(inst->wb_username); + + /* + * Get the username and domain from the configuration + */ + len = tmpl_expand(&authparams.account_name, user_name_buf, sizeof(user_name_buf), + request, inst->wb_username, NULL, NULL); + if (len < 0) { + REDEBUG2("Unable to expand winbind_username"); + goto done; + } + + if (inst->wb_domain) { + len = tmpl_expand(&authparams.domain_name, domain_name_buf, sizeof(domain_name_buf), + request, inst->wb_domain, NULL, NULL); + if (len < 0) { + REDEBUG2("Unable to expand winbind_domain"); + goto done; + } + } else { + RWDEBUG2("No domain specified; authentication may fail because of this"); + } + + + /* + * Build the wbcAuthUserParams structure with what we know + */ + authparams.level = WBC_AUTH_USER_LEVEL_RESPONSE; + authparams.password.response.nt_length = NT_LENGTH; + + memcpy(resp, response, NT_LENGTH); + authparams.password.response.nt_data = resp; + + memcpy(authparams.password.response.challenge, challenge, + sizeof(authparams.password.response.challenge)); + + authparams.parameter_control |= WBC_MSV1_0_ALLOW_MSVCHAPV2 | + WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | + WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT; + + + /* + * Send auth request across to winbind + */ + wb_ctx = fr_connection_get(inst->wb_pool); + if (wb_ctx == NULL) { + RERROR("Unable to get winbind connection from pool"); + goto done; + } + + RDEBUG2("sending authentication request user='%s' domain='%s'", authparams.account_name, + authparams.domain_name); + + err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error); + + 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 + * a few errors that can actually be returned from wbcCtxAuthenticateUserEx. + */ + switch (err) { + case WBC_ERR_SUCCESS: + rcode = 0; + RDEBUG2("Authenticated successfully"); + /* Grab the nthashhash from the result */ + memcpy(nthashhash, info->user_session_key, NT_DIGEST_LENGTH); + break; + case WBC_ERR_WINBIND_NOT_AVAILABLE: + rcode = -2; + RERROR("Unable to contact winbind!"); + RDEBUG2("Check that winbind is running and that FreeRADIUS has"); + RDEBUG2("permission to connect to the winbind privileged socket."); + break; + case WBC_ERR_DOMAIN_NOT_FOUND: + REDEBUG2("Domain not found"); + break; + case WBC_ERR_AUTH_ERROR: + if (!error) { + REDEBUG2("Authentication failed"); + break; + } + + /* + * The password needs to be changed, so set rcode appropriately. + */ + if (error->nt_status == NT_STATUS_PASSWORD_EXPIRED || + error->nt_status == NT_STATUS_PASSWORD_MUST_CHANGE) { + rcode = -648; + } + + /* + * Return the NT_STATUS human readable error string, if there is one. + */ + if (error->display_string) { + REDEBUG2("%s [0x%X]", error->display_string, error->nt_status); + } else { + REDEBUG2("Authentication failed [0x%X]", error->nt_status); + } + break; + default: + /* + * Only errors left are + * WBC_ERR_INVALID_PARAM + * WBC_ERR_NO_MEMORY + * neither of which are particularly likely. + */ + rcode = -2; + if (error && error->display_string) { + REDEBUG2("libwbclient error: wbcErr %d (%s)", err, error->display_string); + } else { + REDEBUG2("libwbclient error: wbcErr %d", err); + } + break; + } + + +done: + if (info) wbcFreeMemory(info); + if (error) wbcFreeMemory(error); + + return rcode; +} + |