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