1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM)
26
27#include <curl/curl.h>
28
29#include "vauth/vauth.h"
30#include "urldata.h"
31#include "curl_ntlm_core.h"
32#include "warnless.h"
33#include "curl_multibyte.h"
34#include "sendf.h"
35
36/* The last #include files should be: */
37#include "curl_memory.h"
38#include "memdebug.h"
39
40/*
41 * Curl_auth_is_ntlm_supported()
42 *
43 * This is used to evaluate if NTLM is supported.
44 *
45 * Parameters: None
46 *
47 * Returns TRUE if NTLM is supported by Windows SSPI.
48 */
49bool Curl_auth_is_ntlm_supported(void)
50{
51 PSecPkgInfo SecurityPackage;
52 SECURITY_STATUS status;
53
54 /* Query the security package for NTLM */
55 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
56 &SecurityPackage);
57
58 /* Release the package buffer as it is not required anymore */
59 if(status == SEC_E_OK) {
60 s_pSecFn->FreeContextBuffer(SecurityPackage);
61 }
62
63 return (status == SEC_E_OK ? TRUE : FALSE);
64}
65
66/*
67 * Curl_auth_create_ntlm_type1_message()
68 *
69 * This is used to generate an already encoded NTLM type-1 message ready for
70 * sending to the recipient.
71 *
72 * Parameters:
73 *
74 * data [in] - The session handle.
75 * userp [in] - The user name in the format User or Domain\User.
76 * passwdp [in] - The user's password.
77 * service [in] - The service type such as http, smtp, pop or imap.
78 * host [in] - The host name.
79 * ntlm [in/out] - The NTLM data struct being used and modified.
80 * out [out] - The result storage.
81 *
82 * Returns CURLE_OK on success.
83 */
84CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
85 const char *userp,
86 const char *passwdp,
87 const char *service,
88 const char *host,
89 struct ntlmdata *ntlm,
90 struct bufref *out)
91{
92 PSecPkgInfo SecurityPackage;
93 SecBuffer type_1_buf;
94 SecBufferDesc type_1_desc;
95 SECURITY_STATUS status;
96 unsigned long attrs;
97 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
98
99 /* Clean up any former leftovers and initialise to defaults */
100 Curl_auth_cleanup_ntlm(ntlm);
101
102 /* Query the security package for NTLM */
103 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
104 &SecurityPackage);
105 if(status != SEC_E_OK) {
106 failf(data, "SSPI: couldn't get auth info");
107 return CURLE_AUTH_ERROR;
108 }
109
110 ntlm->token_max = SecurityPackage->cbMaxToken;
111
112 /* Release the package buffer as it is not required anymore */
113 s_pSecFn->FreeContextBuffer(SecurityPackage);
114
115 /* Allocate our output buffer */
116 ntlm->output_token = malloc(ntlm->token_max);
117 if(!ntlm->output_token)
118 return CURLE_OUT_OF_MEMORY;
119
120 if(userp && *userp) {
121 CURLcode result;
122
123 /* Populate our identity structure */
124 result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
125 if(result)
126 return result;
127
128 /* Allow proper cleanup of the identity structure */
129 ntlm->p_identity = &ntlm->identity;
130 }
131 else
132 /* Use the current Windows user */
133 ntlm->p_identity = NULL;
134
135 /* Allocate our credentials handle */
136 ntlm->credentials = calloc(1, sizeof(CredHandle));
137 if(!ntlm->credentials)
138 return CURLE_OUT_OF_MEMORY;
139
140 /* Acquire our credentials handle */
141 status = s_pSecFn->AcquireCredentialsHandle(NULL,
142 (TCHAR *) TEXT(SP_NAME_NTLM),
143 SECPKG_CRED_OUTBOUND, NULL,
144 ntlm->p_identity, NULL, NULL,
145 ntlm->credentials, &expiry);
146 if(status != SEC_E_OK)
147 return CURLE_LOGIN_DENIED;
148
149 /* Allocate our new context handle */
150 ntlm->context = calloc(1, sizeof(CtxtHandle));
151 if(!ntlm->context)
152 return CURLE_OUT_OF_MEMORY;
153
154 ntlm->spn = Curl_auth_build_spn(service, host, NULL);
155 if(!ntlm->spn)
156 return CURLE_OUT_OF_MEMORY;
157
158 /* Setup the type-1 "output" security buffer */
159 type_1_desc.ulVersion = SECBUFFER_VERSION;
160 type_1_desc.cBuffers = 1;
161 type_1_desc.pBuffers = &type_1_buf;
162 type_1_buf.BufferType = SECBUFFER_TOKEN;
163 type_1_buf.pvBuffer = ntlm->output_token;
164 type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
165
166 /* Generate our type-1 message */
167 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
168 ntlm->spn,
169 0, 0, SECURITY_NETWORK_DREP,
170 NULL, 0,
171 ntlm->context, &type_1_desc,
172 &attrs, &expiry);
173 if(status == SEC_I_COMPLETE_NEEDED ||
174 status == SEC_I_COMPLETE_AND_CONTINUE)
175 s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
176 else if(status == SEC_E_INSUFFICIENT_MEMORY)
177 return CURLE_OUT_OF_MEMORY;
178 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
179 return CURLE_AUTH_ERROR;
180
181 /* Return the response. */
182 Curl_bufref_set(out, ntlm->output_token, type_1_buf.cbBuffer, NULL);
183 return CURLE_OK;
184}
185
186/*
187 * Curl_auth_decode_ntlm_type2_message()
188 *
189 * This is used to decode an already encoded NTLM type-2 message.
190 *
191 * Parameters:
192 *
193 * data [in] - The session handle.
194 * type2 [in] - The type-2 message.
195 * ntlm [in/out] - The NTLM data struct being used and modified.
196 *
197 * Returns CURLE_OK on success.
198 */
199CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
200 const struct bufref *type2,
201 struct ntlmdata *ntlm)
202{
203#if defined(CURL_DISABLE_VERBOSE_STRINGS)
204 (void) data;
205#endif
206
207 /* Ensure we have a valid type-2 message */
208 if(!Curl_bufref_len(type2)) {
209 infof(data, "NTLM handshake failure (empty type-2 message)");
210 return CURLE_BAD_CONTENT_ENCODING;
211 }
212
213 /* Store the challenge for later use */
214 ntlm->input_token = malloc(Curl_bufref_len(type2) + 1);
215 if(!ntlm->input_token)
216 return CURLE_OUT_OF_MEMORY;
217 memcpy(ntlm->input_token, Curl_bufref_ptr(type2), Curl_bufref_len(type2));
218 ntlm->input_token[Curl_bufref_len(type2)] = '\0';
219 ntlm->input_token_len = Curl_bufref_len(type2);
220
221 return CURLE_OK;
222}
223
224/*
225* Curl_auth_create_ntlm_type3_message()
226 * Curl_auth_create_ntlm_type3_message()
227 *
228 * This is used to generate an already encoded NTLM type-3 message ready for
229 * sending to the recipient.
230 *
231 * Parameters:
232 *
233 * data [in] - The session handle.
234 * userp [in] - The user name in the format User or Domain\User.
235 * passwdp [in] - The user's password.
236 * ntlm [in/out] - The NTLM data struct being used and modified.
237 * out [out] - The result storage.
238 *
239 * Returns CURLE_OK on success.
240 */
241CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
242 const char *userp,
243 const char *passwdp,
244 struct ntlmdata *ntlm,
245 struct bufref *out)
246{
247 CURLcode result = CURLE_OK;
248 SecBuffer type_2_bufs[2];
249 SecBuffer type_3_buf;
250 SecBufferDesc type_2_desc;
251 SecBufferDesc type_3_desc;
252 SECURITY_STATUS status;
253 unsigned long attrs;
254 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
255
256#if defined(CURL_DISABLE_VERBOSE_STRINGS)
257 (void) data;
258#endif
259 (void) passwdp;
260 (void) userp;
261
262 /* Setup the type-2 "input" security buffer */
263 type_2_desc.ulVersion = SECBUFFER_VERSION;
264 type_2_desc.cBuffers = 1;
265 type_2_desc.pBuffers = &type_2_bufs[0];
266 type_2_bufs[0].BufferType = SECBUFFER_TOKEN;
267 type_2_bufs[0].pvBuffer = ntlm->input_token;
268 type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len);
269
270#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
271 /* ssl context comes from schannel.
272 * When extended protection is used in IIS server,
273 * we have to pass a second SecBuffer to the SecBufferDesc
274 * otherwise IIS will not pass the authentication (401 response).
275 * Minimum supported version is Windows 7.
276 * https://docs.microsoft.com/en-us/security-updates
277 * /SecurityAdvisories/2009/973811
278 */
279 if(ntlm->sslContext) {
280 SEC_CHANNEL_BINDINGS channelBindings;
281 SecPkgContext_Bindings pkgBindings;
282 pkgBindings.Bindings = &channelBindings;
283 status = s_pSecFn->QueryContextAttributes(
284 ntlm->sslContext,
285 SECPKG_ATTR_ENDPOINT_BINDINGS,
286 &pkgBindings
287 );
288 if(status == SEC_E_OK) {
289 type_2_desc.cBuffers++;
290 type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
291 type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength;
292 type_2_bufs[1].pvBuffer = pkgBindings.Bindings;
293 }
294 }
295#endif
296
297 /* Setup the type-3 "output" security buffer */
298 type_3_desc.ulVersion = SECBUFFER_VERSION;
299 type_3_desc.cBuffers = 1;
300 type_3_desc.pBuffers = &type_3_buf;
301 type_3_buf.BufferType = SECBUFFER_TOKEN;
302 type_3_buf.pvBuffer = ntlm->output_token;
303 type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
304
305 /* Generate our type-3 message */
306 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
307 ntlm->context,
308 ntlm->spn,
309 0, 0, SECURITY_NETWORK_DREP,
310 &type_2_desc,
311 0, ntlm->context,
312 &type_3_desc,
313 &attrs, &expiry);
314 if(status != SEC_E_OK) {
315 infof(data, "NTLM handshake failure (type-3 message): Status=%x",
316 status);
317
318 if(status == SEC_E_INSUFFICIENT_MEMORY)
319 return CURLE_OUT_OF_MEMORY;
320
321 return CURLE_AUTH_ERROR;
322 }
323
324 /* Return the response. */
325 result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer);
326 Curl_auth_cleanup_ntlm(ntlm);
327 return result;
328}
329
330/*
331 * Curl_auth_cleanup_ntlm()
332 *
333 * This is used to clean up the NTLM specific data.
334 *
335 * Parameters:
336 *
337 * ntlm [in/out] - The NTLM data struct being cleaned up.
338 *
339 */
340void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
341{
342 /* Free our security context */
343 if(ntlm->context) {
344 s_pSecFn->DeleteSecurityContext(ntlm->context);
345 free(ntlm->context);
346 ntlm->context = NULL;
347 }
348
349 /* Free our credentials handle */
350 if(ntlm->credentials) {
351 s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
352 free(ntlm->credentials);
353 ntlm->credentials = NULL;
354 }
355
356 /* Free our identity */
357 Curl_sspi_free_identity(ntlm->p_identity);
358 ntlm->p_identity = NULL;
359
360 /* Free the input and output tokens */
361 Curl_safefree(ntlm->input_token);
362 Curl_safefree(ntlm->output_token);
363
364 /* Reset any variables */
365 ntlm->token_max = 0;
366
367 Curl_safefree(ntlm->spn);
368}
369
370#endif /* USE_WINDOWS_SSPI && USE_NTLM */
371