The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
auth_wbclient.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 82578bf2e4accc18e9a605150d7875c2b14371e1 $
19 * @file auth_wbclient.c
20 * @brief NTLM authentication against the wbclient library
21 *
22 * @copyright 2015 Matthew Newton
23 */
24
25RCSID("$Id: 82578bf2e4accc18e9a605150d7875c2b14371e1 $")
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/util/debug.h>
29
30#include <wbclient.h>
31#include <core/ntstatus.h>
32
33#include "rlm_mschap.h"
34#include "mschap.h"
35#include "auth_wbclient.h"
36
37/* Samba does not export this constant yet */
38#ifndef WBC_MSV1_0_ALLOW_MSVCHAPV2
39#define WBC_MSV1_0_ALLOW_MSVCHAPV2 0x00010000
40#endif
41
42#define NT_LENGTH 24
43
44/** Use Winbind to normalise a username
45 *
46 * @param[in] ctx The talloc context where the result is parented from
47 * @param[in] wb_ctx The winbind context
48 * @param[in] dom_name The domain of the user
49 * @param[in] name The username (without the domain) to be normalised
50 * @return
51 * - The username with the casing according to the Winbind remote server.
52 * - NULL if the username could not be found.
53 */
54static char *wbclient_normalise_username(TALLOC_CTX *ctx, struct wbcContext *wb_ctx,
55 char const *dom_name, char const *name)
56{
57 struct wbcDomainSid sid;
58 enum wbcSidType name_type;
59 wbcErr err;
60 char *res_domain = NULL;
61 char *res_name = NULL;
62 char *res = NULL;
63
64 /* Step 1: Convert a name to a sid */
65 err = wbcCtxLookupName(wb_ctx, dom_name, name, &sid, &name_type);
66 if (!WBC_ERROR_IS_OK(err)) return NULL;
67
68 /* Step 2: Convert the sid back to a name */
69 err = wbcCtxLookupSid(wb_ctx, &sid, &res_domain, &res_name, &name_type);
70 if (!WBC_ERROR_IS_OK(err)) return NULL;
71
72 MEM(res = talloc_strdup(ctx, res_name));
73
74 wbcFreeMemory(res_domain);
75 wbcFreeMemory(res_name);
76
77 return res;
78}
79
80/** Check NTLM authentication direct to winbind via Samba's libwbclient library
81 *
82 * @param[in] inst Module instance.
83 * @param[in] request Current request.
84 * @param[in] challenge MS CHAP challenge.
85 * @param[in] response MS CHAP response.
86 * @param[out] nthashhash Hash returned on success.
87 * @param[in] auth_ctx data for current authentication.
88 * @return
89 * - 0 success.
90 * - -1 auth failure.
91 * - -648 password expired.
92 */
94 uint8_t const *challenge, uint8_t const *response,
95 uint8_t nthashhash[NT_DIGEST_LENGTH], mschap_auth_ctx_t *auth_ctx)
96{
97 int ret = -1;
98 winbind_ctx_t *wbctx = NULL;
99 struct wbcContext *wb_ctx = NULL;
100 struct wbcAuthUserParams authparams;
101 wbcErr err;
102 struct wbcAuthUserInfo *info = NULL;
103 struct wbcAuthErrorInfo *error = NULL;
104 uint8_t resp[NT_LENGTH];
105 mschap_auth_call_env_t *env_data = auth_ctx->env_data;
106
107 /*
108 * Clear the auth parameters - this is important, as
109 * there are options that will cause wbcAuthenticateUserEx
110 * to bomb out if not zero.
111 */
112 memset(&authparams, 0, sizeof(authparams));
113
114 if (env_data->wb_domain.type == FR_TYPE_STRING) {
115 authparams.domain_name = env_data->wb_domain.vb_strvalue;
116 } else {
117 RWDEBUG2("No domain specified; authentication may fail because of this");
118 }
119
120 /*
121 * Get the username from the call_env data.
122 */
123 authparams.account_name = env_data->wb_username.vb_strvalue;
124
125 /*
126 * Build the wbcAuthUserParams structure with what we know
127 */
128 authparams.level = WBC_AUTH_USER_LEVEL_RESPONSE;
129 authparams.password.response.nt_length = NT_LENGTH;
130
131 memcpy(resp, response, NT_LENGTH);
132 authparams.password.response.nt_data = resp;
133
134 memcpy(authparams.password.response.challenge, challenge, sizeof(authparams.password.response.challenge));
135
136 authparams.parameter_control |= WBC_MSV1_0_ALLOW_MSVCHAPV2 |
137 WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
138 WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
139
140 /*
141 * Send auth request across to winbind
142 */
143 wbctx = mschap_slab_reserve(auth_ctx->t->slab);
144 if (!wbctx) {
145 RERROR("Unable to get winbind connection from pool");
146 goto finish;
147 }
148 wb_ctx = wbctx->ctx;
149
150 RDEBUG2("Sending authentication request user \"%pV\" domain \"%pV\"",
151 &env_data->wb_username, &env_data->wb_domain);
152
153 err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error);
154 if (err == WBC_ERR_AUTH_ERROR && inst->wb_retry_with_normalised_username) {
155 fr_pair_t *vp_response;
156 fr_pair_t *vp_challenge;
157 fr_pair_t *vp_chap_user_name;
158 char *normalised_username = NULL;
159
160 normalised_username = wbclient_normalise_username(env_data, wb_ctx, authparams.domain_name,
161 authparams.account_name);
162 if (!normalised_username) goto release;
163
164 RDEBUG2("Starting retry, normalised username \"%pV\" -> \"%pV\"",
165 &env_data->wb_username,
166 fr_box_strvalue_buffer(normalised_username));
167
168 if (talloc_memcmp_bstr(authparams.account_name, normalised_username) == 0) goto release;
169
170 authparams.account_name = normalised_username;
171
172 /* Set MS-CHAP-USER-NAME */
173 MEM(pair_update_request(&vp_chap_user_name, attr_ms_chap_user_name) >= 0);
174 fr_pair_value_bstrdup_buffer(vp_chap_user_name, normalised_username, true);
175
176 RDEBUG2("Retrying authentication request user \"%pV\" domain \"%pV\"",
177 fr_box_strvalue_buffer(normalised_username),
178 &env_data->wb_domain);
179
180 /* Recalculate hash */
181 vp_challenge = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
183 if (!vp_challenge) {
184 RERROR("Unable to get %s", env_data->chap_challenge->name);
185 goto release;
186 }
187
188 vp_response = fr_pair_find_by_da_nested(&request->request_pairs, NULL,
190 if (!vp_response) {
191 RERROR("Unable to get MS-CHAP2-Response");
192 goto release;
193 }
194
195 mschap_challenge_hash(authparams.password.response.challenge,
196 vp_response->vp_octets + 2,
197 vp_challenge->vp_octets,
198 vp_chap_user_name->vp_strvalue, vp_chap_user_name->vp_length);
199
200 err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error);
201release:
202 talloc_free(normalised_username);
203 }
204
205 mschap_slab_release(wbctx);
206
207 /*
208 * Try and give some useful feedback on what happened. There are only
209 * a few errors that can actually be returned from wbcCtxAuthenticateUserEx.
210 */
211 switch (err) {
212 case WBC_ERR_SUCCESS:
213 ret = 0;
214 RDEBUG2("Authenticated successfully");
215 /* Grab the nthashhash from the result */
216 memcpy(nthashhash, info->user_session_key, NT_DIGEST_LENGTH);
217 break;
218
219 case WBC_ERR_WINBIND_NOT_AVAILABLE:
220 RERROR("Unable to contact winbind!");
221 RDEBUG2("Check that winbind is running and that FreeRADIUS has");
222 RDEBUG2("permission to connect to the winbind privileged socket.");
223 break;
224
225 case WBC_ERR_DOMAIN_NOT_FOUND:
226 REDEBUG2("Domain not found");
227 break;
228
229 case WBC_ERR_AUTH_ERROR:
230 if (!error) {
231 REDEBUG2("Authentication failed");
232 break;
233 }
234
235 /*
236 * The password needs to be changed, so set ret appropriately.
237 */
238 if (error->nt_status == NT_STATUS_PASSWORD_EXPIRED ||
239 error->nt_status == NT_STATUS_PASSWORD_MUST_CHANGE) {
240 ret = -648;
241 }
242
243 /*
244 * Return the NT_STATUS human readable error string, if there is one.
245 */
246 if (error->display_string) {
247 REDEBUG2("%s [0x%X]", error->display_string, error->nt_status);
248 } else {
249 REDEBUG2("Authentication failed [0x%X]", error->nt_status);
250 }
251 break;
252
253 default:
254 /*
255 * Only errors left are
256 * WBC_ERR_INVALID_PARAM
257 * WBC_ERR_NO_MEMORY
258 * neither of which are particularly likely.
259 */
260 if (error && error->display_string) {
261 REDEBUG2("libwbclient error: wbcErr %d (%s)", err, error->display_string);
262 } else {
263 REDEBUG2("libwbclient error: wbcErr %d", err);
264 }
265 break;
266 }
267
268finish:
269 if (info) wbcFreeMemory(info);
270 if (error) wbcFreeMemory(error);
271
272 return ret;
273}
int do_auth_wbclient(rlm_mschap_t const *inst, request_t *request, uint8_t const *challenge, uint8_t const *response, uint8_t nthashhash[NT_DIGEST_LENGTH], mschap_auth_ctx_t *auth_ctx)
Check NTLM authentication direct to winbind via Samba's libwbclient library.
#define NT_LENGTH
static char * wbclient_normalise_username(TALLOC_CTX *ctx, struct wbcContext *wb_ctx, char const *dom_name, char const *name)
Use Winbind to normalise a username.
#define WBC_MSV1_0_ALLOW_MSVCHAPV2
#define RCSID(id)
Definition build.h:483
#define MEM(x)
Definition debug.h:36
static fr_slen_t err
Definition dict.h:824
#define RWDEBUG2(fmt,...)
Definition log.h:362
#define RERROR(fmt,...)
Definition log.h:298
#define REDEBUG2(fmt,...)
Definition log.h:372
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
unsigned char uint8_t
void mschap_challenge_hash(uint8_t challenge[static MSCHAP_CHALLENGE_LENGTH], uint8_t const peer_challenge[static MSCHAP_PEER_CHALLENGE_LENGTH], uint8_t const auth_challenge[static MSCHAP_PEER_AUTHENTICATOR_CHALLENGE_LENGTH], char const *user_name, size_t user_name_len)
Definition mschap.c:72
#define NT_DIGEST_LENGTH
Definition mschap.h:7
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition pair.c:770
#define pair_update_request(_attr, _da)
#define RDEBUG2(fmt,...)
Definition radclient.h:54
static fr_dict_attr_t const * attr_ms_chap_user_name
fr_value_box_t wb_domain
Definition rlm_mschap.h:96
fr_value_box_t wb_username
Definition rlm_mschap.h:95
tmpl_t const * chap_challenge
Definition rlm_mschap.h:84
tmpl_t const * chap2_response
Definition rlm_mschap.h:86
mschap_auth_call_env_t * env_data
Definition rlm_mschap.h:114
static char const * name
struct wbcContext * ctx
Definition rlm_winbind.h:20
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:812
eap_aka_sim_process_conf_t * inst
fr_pair_value_bstrdup_buffer(vp, eap_session->identity, true)
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
int talloc_memcmp_bstr(char const *a, char const *b)
Compares two talloced char arrays with memcmp.
Definition talloc.c:796
#define fr_box_strvalue_buffer(_val)
Definition value.h:289