1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 2014 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
|
---|
9 | * Copyright (C) 2014 - 2015, Steve Holme, <steve_holme@hotmail.com>.
|
---|
10 | *
|
---|
11 | * This software is licensed as described in the file COPYING, which
|
---|
12 | * you should have received as part of this distribution. The terms
|
---|
13 | * are also available at https://curl.haxx.se/docs/copyright.html.
|
---|
14 | *
|
---|
15 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
16 | * copies of the Software, and permit persons to whom the Software is
|
---|
17 | * furnished to do so, under the terms of the COPYING file.
|
---|
18 | *
|
---|
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
20 | * KIND, either express or implied.
|
---|
21 | *
|
---|
22 | * RFC2617 Basic and Digest Access Authentication
|
---|
23 | * RFC2831 DIGEST-MD5 authentication
|
---|
24 | * RFC4422 Simple Authentication and Security Layer (SASL)
|
---|
25 | * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
|
---|
26 | *
|
---|
27 | ***************************************************************************/
|
---|
28 |
|
---|
29 | #include "curl_setup.h"
|
---|
30 |
|
---|
31 | #if defined(USE_WINDOWS_SSPI)
|
---|
32 |
|
---|
33 | #include <curl/curl.h>
|
---|
34 |
|
---|
35 | #include "curl_sasl.h"
|
---|
36 | #include "urldata.h"
|
---|
37 | #include "curl_base64.h"
|
---|
38 | #include "warnless.h"
|
---|
39 | #include "curl_multibyte.h"
|
---|
40 | #include "sendf.h"
|
---|
41 | #include "strdup.h"
|
---|
42 | #include "curl_printf.h"
|
---|
43 | #include "rawstr.h"
|
---|
44 |
|
---|
45 | /* The last #include files should be: */
|
---|
46 | #include "curl_memory.h"
|
---|
47 | #include "memdebug.h"
|
---|
48 |
|
---|
49 | /*
|
---|
50 | * Curl_sasl_build_spn()
|
---|
51 | *
|
---|
52 | * This is used to build a SPN string in the format service/instance.
|
---|
53 | *
|
---|
54 | * Parameters:
|
---|
55 | *
|
---|
56 | * serivce [in] - The service type such as www, smtp, pop or imap.
|
---|
57 | * instance [in] - The host name or realm.
|
---|
58 | *
|
---|
59 | * Returns a pointer to the newly allocated SPN.
|
---|
60 | */
|
---|
61 | TCHAR *Curl_sasl_build_spn(const char *service, const char *instance)
|
---|
62 | {
|
---|
63 | char *utf8_spn = NULL;
|
---|
64 | TCHAR *tchar_spn = NULL;
|
---|
65 |
|
---|
66 | /* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather
|
---|
67 | than doing this ourselves but the first is only available in Windows XP
|
---|
68 | and Windows Server 2003 and the latter is only available in Windows 2000
|
---|
69 | but not Windows95/98/ME or Windows NT4.0 unless the Active Directory
|
---|
70 | Client Extensions are installed. As such it is far simpler for us to
|
---|
71 | formulate the SPN instead. */
|
---|
72 |
|
---|
73 | /* Allocate our UTF8 based SPN */
|
---|
74 | utf8_spn = aprintf("%s/%s", service, instance);
|
---|
75 | if(!utf8_spn) {
|
---|
76 | return NULL;
|
---|
77 | }
|
---|
78 |
|
---|
79 | /* Allocate our TCHAR based SPN */
|
---|
80 | tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn);
|
---|
81 | if(!tchar_spn) {
|
---|
82 | free(utf8_spn);
|
---|
83 |
|
---|
84 | return NULL;
|
---|
85 | }
|
---|
86 |
|
---|
87 | /* Release the UTF8 variant when operating with Unicode */
|
---|
88 | Curl_unicodefree(utf8_spn);
|
---|
89 |
|
---|
90 | /* Return our newly allocated SPN */
|
---|
91 | return tchar_spn;
|
---|
92 | }
|
---|
93 |
|
---|
94 | #if !defined(CURL_DISABLE_CRYPTO_AUTH)
|
---|
95 | /*
|
---|
96 | * Curl_sasl_create_digest_md5_message()
|
---|
97 | *
|
---|
98 | * This is used to generate an already encoded DIGEST-MD5 response message
|
---|
99 | * ready for sending to the recipient.
|
---|
100 | *
|
---|
101 | * Parameters:
|
---|
102 | *
|
---|
103 | * data [in] - The session handle.
|
---|
104 | * chlg64 [in] - The base64 encoded challenge message.
|
---|
105 | * userp [in] - The user name in the format User or Domain\User.
|
---|
106 | * passdwp [in] - The user's password.
|
---|
107 | * service [in] - The service type such as www, smtp, pop or imap.
|
---|
108 | * outptr [in/out] - The address where a pointer to newly allocated memory
|
---|
109 | * holding the result will be stored upon completion.
|
---|
110 | * outlen [out] - The length of the output message.
|
---|
111 | *
|
---|
112 | * Returns CURLE_OK on success.
|
---|
113 | */
|
---|
114 | CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
|
---|
115 | const char *chlg64,
|
---|
116 | const char *userp,
|
---|
117 | const char *passwdp,
|
---|
118 | const char *service,
|
---|
119 | char **outptr, size_t *outlen)
|
---|
120 | {
|
---|
121 | CURLcode result = CURLE_OK;
|
---|
122 | TCHAR *spn = NULL;
|
---|
123 | size_t chlglen = 0;
|
---|
124 | size_t token_max = 0;
|
---|
125 | unsigned char *input_token = NULL;
|
---|
126 | unsigned char *output_token = NULL;
|
---|
127 | CredHandle credentials;
|
---|
128 | CtxtHandle context;
|
---|
129 | PSecPkgInfo SecurityPackage;
|
---|
130 | SEC_WINNT_AUTH_IDENTITY identity;
|
---|
131 | SEC_WINNT_AUTH_IDENTITY *p_identity;
|
---|
132 | SecBuffer chlg_buf;
|
---|
133 | SecBuffer resp_buf;
|
---|
134 | SecBufferDesc chlg_desc;
|
---|
135 | SecBufferDesc resp_desc;
|
---|
136 | SECURITY_STATUS status;
|
---|
137 | unsigned long attrs;
|
---|
138 | TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
|
---|
139 |
|
---|
140 | /* Decode the base-64 encoded challenge message */
|
---|
141 | if(strlen(chlg64) && *chlg64 != '=') {
|
---|
142 | result = Curl_base64_decode(chlg64, &input_token, &chlglen);
|
---|
143 | if(result)
|
---|
144 | return result;
|
---|
145 | }
|
---|
146 |
|
---|
147 | /* Ensure we have a valid challenge message */
|
---|
148 | if(!input_token) {
|
---|
149 | infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
|
---|
150 |
|
---|
151 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /* Query the security package for DigestSSP */
|
---|
155 | status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
|
---|
156 | &SecurityPackage);
|
---|
157 | if(status != SEC_E_OK) {
|
---|
158 | free(input_token);
|
---|
159 |
|
---|
160 | return CURLE_NOT_BUILT_IN;
|
---|
161 | }
|
---|
162 |
|
---|
163 | token_max = SecurityPackage->cbMaxToken;
|
---|
164 |
|
---|
165 | /* Release the package buffer as it is not required anymore */
|
---|
166 | s_pSecFn->FreeContextBuffer(SecurityPackage);
|
---|
167 |
|
---|
168 | /* Allocate our response buffer */
|
---|
169 | output_token = malloc(token_max);
|
---|
170 | if(!output_token) {
|
---|
171 | free(input_token);
|
---|
172 |
|
---|
173 | return CURLE_OUT_OF_MEMORY;
|
---|
174 | }
|
---|
175 |
|
---|
176 | /* Generate our SPN */
|
---|
177 | spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
|
---|
178 | if(!spn) {
|
---|
179 | free(output_token);
|
---|
180 | free(input_token);
|
---|
181 |
|
---|
182 | return CURLE_OUT_OF_MEMORY;
|
---|
183 | }
|
---|
184 |
|
---|
185 | if(userp && *userp) {
|
---|
186 | /* Populate our identity structure */
|
---|
187 | result = Curl_create_sspi_identity(userp, passwdp, &identity);
|
---|
188 | if(result) {
|
---|
189 | free(spn);
|
---|
190 | free(output_token);
|
---|
191 | free(input_token);
|
---|
192 |
|
---|
193 | return result;
|
---|
194 | }
|
---|
195 |
|
---|
196 | /* Allow proper cleanup of the identity structure */
|
---|
197 | p_identity = &identity;
|
---|
198 | }
|
---|
199 | else
|
---|
200 | /* Use the current Windows user */
|
---|
201 | p_identity = NULL;
|
---|
202 |
|
---|
203 | /* Acquire our credentials handle */
|
---|
204 | status = s_pSecFn->AcquireCredentialsHandle(NULL,
|
---|
205 | (TCHAR *) TEXT(SP_NAME_DIGEST),
|
---|
206 | SECPKG_CRED_OUTBOUND, NULL,
|
---|
207 | p_identity, NULL, NULL,
|
---|
208 | &credentials, &expiry);
|
---|
209 |
|
---|
210 | if(status != SEC_E_OK) {
|
---|
211 | Curl_sspi_free_identity(p_identity);
|
---|
212 | free(spn);
|
---|
213 | free(output_token);
|
---|
214 | free(input_token);
|
---|
215 |
|
---|
216 | return CURLE_LOGIN_DENIED;
|
---|
217 | }
|
---|
218 |
|
---|
219 | /* Setup the challenge "input" security buffer */
|
---|
220 | chlg_desc.ulVersion = SECBUFFER_VERSION;
|
---|
221 | chlg_desc.cBuffers = 1;
|
---|
222 | chlg_desc.pBuffers = &chlg_buf;
|
---|
223 | chlg_buf.BufferType = SECBUFFER_TOKEN;
|
---|
224 | chlg_buf.pvBuffer = input_token;
|
---|
225 | chlg_buf.cbBuffer = curlx_uztoul(chlglen);
|
---|
226 |
|
---|
227 | /* Setup the response "output" security buffer */
|
---|
228 | resp_desc.ulVersion = SECBUFFER_VERSION;
|
---|
229 | resp_desc.cBuffers = 1;
|
---|
230 | resp_desc.pBuffers = &resp_buf;
|
---|
231 | resp_buf.BufferType = SECBUFFER_TOKEN;
|
---|
232 | resp_buf.pvBuffer = output_token;
|
---|
233 | resp_buf.cbBuffer = curlx_uztoul(token_max);
|
---|
234 |
|
---|
235 | /* Generate our response message */
|
---|
236 | status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
|
---|
237 | 0, 0, 0, &chlg_desc, 0,
|
---|
238 | &context, &resp_desc, &attrs,
|
---|
239 | &expiry);
|
---|
240 |
|
---|
241 | if(status == SEC_I_COMPLETE_NEEDED ||
|
---|
242 | status == SEC_I_COMPLETE_AND_CONTINUE)
|
---|
243 | s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
|
---|
244 | else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
|
---|
245 | s_pSecFn->FreeCredentialsHandle(&credentials);
|
---|
246 | Curl_sspi_free_identity(p_identity);
|
---|
247 | free(spn);
|
---|
248 | free(output_token);
|
---|
249 | free(input_token);
|
---|
250 |
|
---|
251 | return CURLE_RECV_ERROR;
|
---|
252 | }
|
---|
253 |
|
---|
254 | /* Base64 encode the response */
|
---|
255 | result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
|
---|
256 | outptr, outlen);
|
---|
257 |
|
---|
258 | /* Free our handles */
|
---|
259 | s_pSecFn->DeleteSecurityContext(&context);
|
---|
260 | s_pSecFn->FreeCredentialsHandle(&credentials);
|
---|
261 |
|
---|
262 | /* Free the identity structure */
|
---|
263 | Curl_sspi_free_identity(p_identity);
|
---|
264 |
|
---|
265 | /* Free the SPN */
|
---|
266 | free(spn);
|
---|
267 |
|
---|
268 | /* Free the response buffer */
|
---|
269 | free(output_token);
|
---|
270 |
|
---|
271 | /* Free the decoded challenge message */
|
---|
272 | free(input_token);
|
---|
273 |
|
---|
274 | return result;
|
---|
275 | }
|
---|
276 |
|
---|
277 | /*
|
---|
278 | * Curl_override_sspi_http_realm()
|
---|
279 | *
|
---|
280 | * This is used to populate the domain in a SSPI identity structure
|
---|
281 | * The realm is extracted from the challenge message and used as the
|
---|
282 | * domain if it is not already explicitly set.
|
---|
283 | *
|
---|
284 | * Parameters:
|
---|
285 | *
|
---|
286 | * chlg [in] - The challenge message.
|
---|
287 | * identity [in/out] - The identity structure.
|
---|
288 | *
|
---|
289 | * Returns CURLE_OK on success.
|
---|
290 | */
|
---|
291 | CURLcode Curl_override_sspi_http_realm(const char *chlg,
|
---|
292 | SEC_WINNT_AUTH_IDENTITY *identity)
|
---|
293 | {
|
---|
294 | xcharp_u domain, dup_domain;
|
---|
295 |
|
---|
296 | /* If domain is blank or unset, check challenge message for realm */
|
---|
297 | if(!identity->Domain || !identity->DomainLength) {
|
---|
298 | for(;;) {
|
---|
299 | char value[DIGEST_MAX_VALUE_LENGTH];
|
---|
300 | char content[DIGEST_MAX_CONTENT_LENGTH];
|
---|
301 |
|
---|
302 | /* Pass all additional spaces here */
|
---|
303 | while(*chlg && ISSPACE(*chlg))
|
---|
304 | chlg++;
|
---|
305 |
|
---|
306 | /* Extract a value=content pair */
|
---|
307 | if(!Curl_sasl_digest_get_pair(chlg, value, content, &chlg)) {
|
---|
308 | if(Curl_raw_equal(value, "realm")) {
|
---|
309 |
|
---|
310 | /* Setup identity's domain and length */
|
---|
311 | domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *)content);
|
---|
312 | if(!domain.tchar_ptr)
|
---|
313 | return CURLE_OUT_OF_MEMORY;
|
---|
314 | dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
|
---|
315 | if(!dup_domain.tchar_ptr) {
|
---|
316 | Curl_unicodefree(domain.tchar_ptr);
|
---|
317 | return CURLE_OUT_OF_MEMORY;
|
---|
318 | }
|
---|
319 | free(identity->Domain);
|
---|
320 | identity->Domain = dup_domain.tbyte_ptr;
|
---|
321 | identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
|
---|
322 | dup_domain.tchar_ptr = NULL;
|
---|
323 |
|
---|
324 | Curl_unicodefree(domain.tchar_ptr);
|
---|
325 | }
|
---|
326 | else {
|
---|
327 | /* unknown specifier, ignore it! */
|
---|
328 | }
|
---|
329 | }
|
---|
330 | else
|
---|
331 | break; /* we're done here */
|
---|
332 |
|
---|
333 | /* Pass all additional spaces here */
|
---|
334 | while(*chlg && ISSPACE(*chlg))
|
---|
335 | chlg++;
|
---|
336 |
|
---|
337 | /* Allow the list to be comma-separated */
|
---|
338 | if(',' == *chlg)
|
---|
339 | chlg++;
|
---|
340 | }
|
---|
341 | }
|
---|
342 |
|
---|
343 | return CURLE_OK;
|
---|
344 | }
|
---|
345 |
|
---|
346 | /*
|
---|
347 | * Curl_sasl_decode_digest_http_message()
|
---|
348 | *
|
---|
349 | * This is used to decode a HTTP DIGEST challenge message into the seperate
|
---|
350 | * attributes.
|
---|
351 | *
|
---|
352 | * Parameters:
|
---|
353 | *
|
---|
354 | * chlg [in] - The challenge message.
|
---|
355 | * digest [in/out] - The digest data struct being used and modified.
|
---|
356 | *
|
---|
357 | * Returns CURLE_OK on success.
|
---|
358 | */
|
---|
359 | CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
|
---|
360 | struct digestdata *digest)
|
---|
361 | {
|
---|
362 | size_t chlglen = strlen(chlg);
|
---|
363 |
|
---|
364 | /* We had an input token before and we got another one now. This means we
|
---|
365 | provided bad credentials in the previous request. */
|
---|
366 | if(digest->input_token)
|
---|
367 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
368 |
|
---|
369 | /* Simply store the challenge for use later */
|
---|
370 | digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
|
---|
371 | if(!digest->input_token)
|
---|
372 | return CURLE_OUT_OF_MEMORY;
|
---|
373 |
|
---|
374 | digest->input_token_len = chlglen;
|
---|
375 |
|
---|
376 | return CURLE_OK;
|
---|
377 | }
|
---|
378 |
|
---|
379 | /*
|
---|
380 | * Curl_sasl_create_digest_http_message()
|
---|
381 | *
|
---|
382 | * This is used to generate a HTTP DIGEST response message ready for sending
|
---|
383 | * to the recipient.
|
---|
384 | *
|
---|
385 | * Parameters:
|
---|
386 | *
|
---|
387 | * data [in] - The session handle.
|
---|
388 | * userp [in] - The user name in the format User or Domain\User.
|
---|
389 | * passdwp [in] - The user's password.
|
---|
390 | * request [in] - The HTTP request.
|
---|
391 | * uripath [in] - The path of the HTTP uri.
|
---|
392 | * digest [in/out] - The digest data struct being used and modified.
|
---|
393 | * outptr [in/out] - The address where a pointer to newly allocated memory
|
---|
394 | * holding the result will be stored upon completion.
|
---|
395 | * outlen [out] - The length of the output message.
|
---|
396 | *
|
---|
397 | * Returns CURLE_OK on success.
|
---|
398 | */
|
---|
399 | CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
|
---|
400 | const char *userp,
|
---|
401 | const char *passwdp,
|
---|
402 | const unsigned char *request,
|
---|
403 | const unsigned char *uripath,
|
---|
404 | struct digestdata *digest,
|
---|
405 | char **outptr, size_t *outlen)
|
---|
406 | {
|
---|
407 | size_t token_max;
|
---|
408 | CredHandle credentials;
|
---|
409 | CtxtHandle context;
|
---|
410 | char *resp;
|
---|
411 | BYTE *output_token;
|
---|
412 | PSecPkgInfo SecurityPackage;
|
---|
413 | SEC_WINNT_AUTH_IDENTITY identity;
|
---|
414 | SEC_WINNT_AUTH_IDENTITY *p_identity;
|
---|
415 | SecBuffer chlg_buf[3];
|
---|
416 | SecBuffer resp_buf;
|
---|
417 | SecBufferDesc chlg_desc;
|
---|
418 | SecBufferDesc resp_desc;
|
---|
419 | SECURITY_STATUS status;
|
---|
420 | unsigned long attrs;
|
---|
421 | TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
|
---|
422 | TCHAR *spn;
|
---|
423 |
|
---|
424 | (void) data;
|
---|
425 |
|
---|
426 | /* Query the security package for DigestSSP */
|
---|
427 | status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
|
---|
428 | &SecurityPackage);
|
---|
429 | if(status != SEC_E_OK)
|
---|
430 | return CURLE_NOT_BUILT_IN;
|
---|
431 |
|
---|
432 | token_max = SecurityPackage->cbMaxToken;
|
---|
433 |
|
---|
434 | /* Release the package buffer as it is not required anymore */
|
---|
435 | s_pSecFn->FreeContextBuffer(SecurityPackage);
|
---|
436 |
|
---|
437 | /* Allocate the output buffer according to the max token size as indicated
|
---|
438 | by the security package */
|
---|
439 | output_token = malloc(token_max);
|
---|
440 | if(!output_token)
|
---|
441 | return CURLE_OUT_OF_MEMORY;
|
---|
442 |
|
---|
443 | if(userp && *userp) {
|
---|
444 | /* Populate our identity structure */
|
---|
445 | if(Curl_create_sspi_identity(userp, passwdp, &identity))
|
---|
446 | return CURLE_OUT_OF_MEMORY;
|
---|
447 |
|
---|
448 | /* Populate our identity domain */
|
---|
449 | if(Curl_override_sspi_http_realm((const char*)digest->input_token,
|
---|
450 | &identity))
|
---|
451 | return CURLE_OUT_OF_MEMORY;
|
---|
452 |
|
---|
453 | /* Allow proper cleanup of the identity structure */
|
---|
454 | p_identity = &identity;
|
---|
455 | }
|
---|
456 | else
|
---|
457 | /* Use the current Windows user */
|
---|
458 | p_identity = NULL;
|
---|
459 |
|
---|
460 | /* Acquire our credentials handle */
|
---|
461 | status = s_pSecFn->AcquireCredentialsHandle(NULL,
|
---|
462 | (TCHAR *) TEXT(SP_NAME_DIGEST),
|
---|
463 | SECPKG_CRED_OUTBOUND, NULL,
|
---|
464 | p_identity, NULL, NULL,
|
---|
465 | &credentials, &expiry);
|
---|
466 | if(status != SEC_E_OK) {
|
---|
467 | Curl_sspi_free_identity(p_identity);
|
---|
468 | free(output_token);
|
---|
469 |
|
---|
470 | return CURLE_LOGIN_DENIED;
|
---|
471 | }
|
---|
472 |
|
---|
473 | /* Setup the challenge "input" security buffer if present */
|
---|
474 | chlg_desc.ulVersion = SECBUFFER_VERSION;
|
---|
475 | chlg_desc.cBuffers = 3;
|
---|
476 | chlg_desc.pBuffers = chlg_buf;
|
---|
477 | chlg_buf[0].BufferType = SECBUFFER_TOKEN;
|
---|
478 | chlg_buf[0].pvBuffer = digest->input_token;
|
---|
479 | chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
|
---|
480 | chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
|
---|
481 | chlg_buf[1].pvBuffer = (void *)request;
|
---|
482 | chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
|
---|
483 | chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
|
---|
484 | chlg_buf[2].pvBuffer = NULL;
|
---|
485 | chlg_buf[2].cbBuffer = 0;
|
---|
486 |
|
---|
487 | /* Setup the response "output" security buffer */
|
---|
488 | resp_desc.ulVersion = SECBUFFER_VERSION;
|
---|
489 | resp_desc.cBuffers = 1;
|
---|
490 | resp_desc.pBuffers = &resp_buf;
|
---|
491 | resp_buf.BufferType = SECBUFFER_TOKEN;
|
---|
492 | resp_buf.pvBuffer = output_token;
|
---|
493 | resp_buf.cbBuffer = curlx_uztoul(token_max);
|
---|
494 |
|
---|
495 | spn = Curl_convert_UTF8_to_tchar((char *) uripath);
|
---|
496 | if(!spn) {
|
---|
497 | Curl_sspi_free_identity(p_identity);
|
---|
498 | free(output_token);
|
---|
499 |
|
---|
500 | return CURLE_OUT_OF_MEMORY;
|
---|
501 | }
|
---|
502 |
|
---|
503 | /* Generate our reponse message */
|
---|
504 | status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
|
---|
505 | spn,
|
---|
506 | ISC_REQ_USE_HTTP_STYLE, 0, 0,
|
---|
507 | &chlg_desc, 0, &context,
|
---|
508 | &resp_desc, &attrs, &expiry);
|
---|
509 | Curl_unicodefree(spn);
|
---|
510 |
|
---|
511 | if(status == SEC_I_COMPLETE_NEEDED ||
|
---|
512 | status == SEC_I_COMPLETE_AND_CONTINUE)
|
---|
513 | s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
|
---|
514 | else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
|
---|
515 | s_pSecFn->FreeCredentialsHandle(&credentials);
|
---|
516 |
|
---|
517 | Curl_sspi_free_identity(p_identity);
|
---|
518 | free(output_token);
|
---|
519 |
|
---|
520 | return CURLE_OUT_OF_MEMORY;
|
---|
521 | }
|
---|
522 |
|
---|
523 | resp = malloc(resp_buf.cbBuffer + 1);
|
---|
524 | if(!resp) {
|
---|
525 | s_pSecFn->DeleteSecurityContext(&context);
|
---|
526 | s_pSecFn->FreeCredentialsHandle(&credentials);
|
---|
527 |
|
---|
528 | Curl_sspi_free_identity(p_identity);
|
---|
529 | free(output_token);
|
---|
530 |
|
---|
531 | return CURLE_OUT_OF_MEMORY;
|
---|
532 | }
|
---|
533 |
|
---|
534 | /* Copy the generated reponse */
|
---|
535 | memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
|
---|
536 | resp[resp_buf.cbBuffer] = 0x00;
|
---|
537 |
|
---|
538 | /* Return the response */
|
---|
539 | *outptr = resp;
|
---|
540 | *outlen = resp_buf.cbBuffer;
|
---|
541 |
|
---|
542 | /* Free our handles */
|
---|
543 | s_pSecFn->DeleteSecurityContext(&context);
|
---|
544 | s_pSecFn->FreeCredentialsHandle(&credentials);
|
---|
545 |
|
---|
546 | /* Free the identity structure */
|
---|
547 | Curl_sspi_free_identity(p_identity);
|
---|
548 |
|
---|
549 | /* Free the response buffer */
|
---|
550 | free(output_token);
|
---|
551 |
|
---|
552 | return CURLE_OK;
|
---|
553 | }
|
---|
554 |
|
---|
555 | /*
|
---|
556 | * Curl_sasl_digest_cleanup()
|
---|
557 | *
|
---|
558 | * This is used to clean up the digest specific data.
|
---|
559 | *
|
---|
560 | * Parameters:
|
---|
561 | *
|
---|
562 | * digest [in/out] - The digest data struct being cleaned up.
|
---|
563 | *
|
---|
564 | */
|
---|
565 | void Curl_sasl_digest_cleanup(struct digestdata *digest)
|
---|
566 | {
|
---|
567 | /* Free the input token */
|
---|
568 | Curl_safefree(digest->input_token);
|
---|
569 |
|
---|
570 | /* Reset any variables */
|
---|
571 | digest->input_token_len = 0;
|
---|
572 | }
|
---|
573 | #endif /* !CURL_DISABLE_CRYPTO_AUTH */
|
---|
574 |
|
---|
575 | #if defined USE_NTLM
|
---|
576 | /*
|
---|
577 | * Curl_sasl_create_ntlm_type1_message()
|
---|
578 | *
|
---|
579 | * This is used to generate an already encoded NTLM type-1 message ready for
|
---|
580 | * sending to the recipient.
|
---|
581 | *
|
---|
582 | * Parameters:
|
---|
583 | *
|
---|
584 | * userp [in] - The user name in the format User or Domain\User.
|
---|
585 | * passdwp [in] - The user's password.
|
---|
586 | * ntlm [in/out] - The ntlm data struct being used and modified.
|
---|
587 | * outptr [in/out] - The address where a pointer to newly allocated memory
|
---|
588 | * holding the result will be stored upon completion.
|
---|
589 | * outlen [out] - The length of the output message.
|
---|
590 | *
|
---|
591 | * Returns CURLE_OK on success.
|
---|
592 | */
|
---|
593 | CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
|
---|
594 | const char *passwdp,
|
---|
595 | struct ntlmdata *ntlm,
|
---|
596 | char **outptr, size_t *outlen)
|
---|
597 | {
|
---|
598 | PSecPkgInfo SecurityPackage;
|
---|
599 | SecBuffer type_1_buf;
|
---|
600 | SecBufferDesc type_1_desc;
|
---|
601 | SECURITY_STATUS status;
|
---|
602 | unsigned long attrs;
|
---|
603 | TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
|
---|
604 |
|
---|
605 | /* Clean up any former leftovers and initialise to defaults */
|
---|
606 | Curl_sasl_ntlm_cleanup(ntlm);
|
---|
607 |
|
---|
608 | /* Query the security package for NTLM */
|
---|
609 | status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
|
---|
610 | &SecurityPackage);
|
---|
611 | if(status != SEC_E_OK)
|
---|
612 | return CURLE_NOT_BUILT_IN;
|
---|
613 |
|
---|
614 | ntlm->token_max = SecurityPackage->cbMaxToken;
|
---|
615 |
|
---|
616 | /* Release the package buffer as it is not required anymore */
|
---|
617 | s_pSecFn->FreeContextBuffer(SecurityPackage);
|
---|
618 |
|
---|
619 | /* Allocate our output buffer */
|
---|
620 | ntlm->output_token = malloc(ntlm->token_max);
|
---|
621 | if(!ntlm->output_token)
|
---|
622 | return CURLE_OUT_OF_MEMORY;
|
---|
623 |
|
---|
624 | if(userp && *userp) {
|
---|
625 | CURLcode result;
|
---|
626 |
|
---|
627 | /* Populate our identity structure */
|
---|
628 | result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
|
---|
629 | if(result)
|
---|
630 | return result;
|
---|
631 |
|
---|
632 | /* Allow proper cleanup of the identity structure */
|
---|
633 | ntlm->p_identity = &ntlm->identity;
|
---|
634 | }
|
---|
635 | else
|
---|
636 | /* Use the current Windows user */
|
---|
637 | ntlm->p_identity = NULL;
|
---|
638 |
|
---|
639 | /* Allocate our credentials handle */
|
---|
640 | ntlm->credentials = malloc(sizeof(CredHandle));
|
---|
641 | if(!ntlm->credentials)
|
---|
642 | return CURLE_OUT_OF_MEMORY;
|
---|
643 |
|
---|
644 | memset(ntlm->credentials, 0, sizeof(CredHandle));
|
---|
645 |
|
---|
646 | /* Acquire our credentials handle */
|
---|
647 | status = s_pSecFn->AcquireCredentialsHandle(NULL,
|
---|
648 | (TCHAR *) TEXT(SP_NAME_NTLM),
|
---|
649 | SECPKG_CRED_OUTBOUND, NULL,
|
---|
650 | ntlm->p_identity, NULL, NULL,
|
---|
651 | ntlm->credentials, &expiry);
|
---|
652 | if(status != SEC_E_OK)
|
---|
653 | return CURLE_LOGIN_DENIED;
|
---|
654 |
|
---|
655 | /* Allocate our new context handle */
|
---|
656 | ntlm->context = malloc(sizeof(CtxtHandle));
|
---|
657 | if(!ntlm->context)
|
---|
658 | return CURLE_OUT_OF_MEMORY;
|
---|
659 |
|
---|
660 | memset(ntlm->context, 0, sizeof(CtxtHandle));
|
---|
661 |
|
---|
662 | /* Setup the type-1 "output" security buffer */
|
---|
663 | type_1_desc.ulVersion = SECBUFFER_VERSION;
|
---|
664 | type_1_desc.cBuffers = 1;
|
---|
665 | type_1_desc.pBuffers = &type_1_buf;
|
---|
666 | type_1_buf.BufferType = SECBUFFER_TOKEN;
|
---|
667 | type_1_buf.pvBuffer = ntlm->output_token;
|
---|
668 | type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
|
---|
669 |
|
---|
670 | /* Generate our type-1 message */
|
---|
671 | status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
|
---|
672 | (TCHAR *) TEXT(""),
|
---|
673 | 0, 0, SECURITY_NETWORK_DREP,
|
---|
674 | NULL, 0,
|
---|
675 | ntlm->context, &type_1_desc,
|
---|
676 | &attrs, &expiry);
|
---|
677 | if(status == SEC_I_COMPLETE_NEEDED ||
|
---|
678 | status == SEC_I_COMPLETE_AND_CONTINUE)
|
---|
679 | s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
|
---|
680 | else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
|
---|
681 | return CURLE_RECV_ERROR;
|
---|
682 |
|
---|
683 | /* Base64 encode the response */
|
---|
684 | return Curl_base64_encode(NULL, (char *) ntlm->output_token,
|
---|
685 | type_1_buf.cbBuffer, outptr, outlen);
|
---|
686 | }
|
---|
687 |
|
---|
688 | /*
|
---|
689 | * Curl_sasl_decode_ntlm_type2_message()
|
---|
690 | *
|
---|
691 | * This is used to decode an already encoded NTLM type-2 message.
|
---|
692 | *
|
---|
693 | * Parameters:
|
---|
694 | *
|
---|
695 | * data [in] - The session handle.
|
---|
696 | * type2msg [in] - The base64 encoded type-2 message.
|
---|
697 | * ntlm [in/out] - The ntlm data struct being used and modified.
|
---|
698 | *
|
---|
699 | * Returns CURLE_OK on success.
|
---|
700 | */
|
---|
701 | CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
|
---|
702 | const char *type2msg,
|
---|
703 | struct ntlmdata *ntlm)
|
---|
704 | {
|
---|
705 | CURLcode result = CURLE_OK;
|
---|
706 | unsigned char *type2 = NULL;
|
---|
707 | size_t type2_len = 0;
|
---|
708 |
|
---|
709 | #if defined(CURL_DISABLE_VERBOSE_STRINGS)
|
---|
710 | (void) data;
|
---|
711 | #endif
|
---|
712 |
|
---|
713 | /* Decode the base-64 encoded type-2 message */
|
---|
714 | if(strlen(type2msg) && *type2msg != '=') {
|
---|
715 | result = Curl_base64_decode(type2msg, &type2, &type2_len);
|
---|
716 | if(result)
|
---|
717 | return result;
|
---|
718 | }
|
---|
719 |
|
---|
720 | /* Ensure we have a valid type-2 message */
|
---|
721 | if(!type2) {
|
---|
722 | infof(data, "NTLM handshake failure (empty type-2 message)\n");
|
---|
723 |
|
---|
724 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
725 | }
|
---|
726 |
|
---|
727 | /* Simply store the challenge for use later */
|
---|
728 | ntlm->input_token = type2;
|
---|
729 | ntlm->input_token_len = type2_len;
|
---|
730 |
|
---|
731 | return result;
|
---|
732 | }
|
---|
733 |
|
---|
734 | /*
|
---|
735 | * Curl_sasl_create_ntlm_type3_message()
|
---|
736 | *
|
---|
737 | * This is used to generate an already encoded NTLM type-3 message ready for
|
---|
738 | * sending to the recipient.
|
---|
739 | *
|
---|
740 | * Parameters:
|
---|
741 | *
|
---|
742 | * data [in] - The session handle.
|
---|
743 | * userp [in] - The user name in the format User or Domain\User.
|
---|
744 | * passdwp [in] - The user's password.
|
---|
745 | * ntlm [in/out] - The ntlm data struct being used and modified.
|
---|
746 | * outptr [in/out] - The address where a pointer to newly allocated memory
|
---|
747 | * holding the result will be stored upon completion.
|
---|
748 | * outlen [out] - The length of the output message.
|
---|
749 | *
|
---|
750 | * Returns CURLE_OK on success.
|
---|
751 | */
|
---|
752 | CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
|
---|
753 | const char *userp,
|
---|
754 | const char *passwdp,
|
---|
755 | struct ntlmdata *ntlm,
|
---|
756 | char **outptr, size_t *outlen)
|
---|
757 | {
|
---|
758 | CURLcode result = CURLE_OK;
|
---|
759 | SecBuffer type_2_buf;
|
---|
760 | SecBuffer type_3_buf;
|
---|
761 | SecBufferDesc type_2_desc;
|
---|
762 | SecBufferDesc type_3_desc;
|
---|
763 | SECURITY_STATUS status;
|
---|
764 | unsigned long attrs;
|
---|
765 | TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
|
---|
766 |
|
---|
767 | (void) passwdp;
|
---|
768 | (void) userp;
|
---|
769 |
|
---|
770 | /* Setup the type-2 "input" security buffer */
|
---|
771 | type_2_desc.ulVersion = SECBUFFER_VERSION;
|
---|
772 | type_2_desc.cBuffers = 1;
|
---|
773 | type_2_desc.pBuffers = &type_2_buf;
|
---|
774 | type_2_buf.BufferType = SECBUFFER_TOKEN;
|
---|
775 | type_2_buf.pvBuffer = ntlm->input_token;
|
---|
776 | type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len);
|
---|
777 |
|
---|
778 | /* Setup the type-3 "output" security buffer */
|
---|
779 | type_3_desc.ulVersion = SECBUFFER_VERSION;
|
---|
780 | type_3_desc.cBuffers = 1;
|
---|
781 | type_3_desc.pBuffers = &type_3_buf;
|
---|
782 | type_3_buf.BufferType = SECBUFFER_TOKEN;
|
---|
783 | type_3_buf.pvBuffer = ntlm->output_token;
|
---|
784 | type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
|
---|
785 |
|
---|
786 | /* Generate our type-3 message */
|
---|
787 | status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
|
---|
788 | ntlm->context,
|
---|
789 | (TCHAR *) TEXT(""),
|
---|
790 | 0, 0, SECURITY_NETWORK_DREP,
|
---|
791 | &type_2_desc,
|
---|
792 | 0, ntlm->context,
|
---|
793 | &type_3_desc,
|
---|
794 | &attrs, &expiry);
|
---|
795 | if(status != SEC_E_OK) {
|
---|
796 | infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
|
---|
797 | status);
|
---|
798 |
|
---|
799 | return CURLE_RECV_ERROR;
|
---|
800 | }
|
---|
801 |
|
---|
802 | /* Base64 encode the response */
|
---|
803 | result = Curl_base64_encode(data, (char *) ntlm->output_token,
|
---|
804 | type_3_buf.cbBuffer, outptr, outlen);
|
---|
805 |
|
---|
806 | Curl_sasl_ntlm_cleanup(ntlm);
|
---|
807 |
|
---|
808 | return result;
|
---|
809 | }
|
---|
810 |
|
---|
811 | /*
|
---|
812 | * Curl_sasl_ntlm_cleanup()
|
---|
813 | *
|
---|
814 | * This is used to clean up the ntlm specific data.
|
---|
815 | *
|
---|
816 | * Parameters:
|
---|
817 | *
|
---|
818 | * ntlm [in/out] - The ntlm data struct being cleaned up.
|
---|
819 | *
|
---|
820 | */
|
---|
821 | void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
|
---|
822 | {
|
---|
823 | /* Free our security context */
|
---|
824 | if(ntlm->context) {
|
---|
825 | s_pSecFn->DeleteSecurityContext(ntlm->context);
|
---|
826 | free(ntlm->context);
|
---|
827 | ntlm->context = NULL;
|
---|
828 | }
|
---|
829 |
|
---|
830 | /* Free our credentials handle */
|
---|
831 | if(ntlm->credentials) {
|
---|
832 | s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
|
---|
833 | free(ntlm->credentials);
|
---|
834 | ntlm->credentials = NULL;
|
---|
835 | }
|
---|
836 |
|
---|
837 | /* Free our identity */
|
---|
838 | Curl_sspi_free_identity(ntlm->p_identity);
|
---|
839 | ntlm->p_identity = NULL;
|
---|
840 |
|
---|
841 | /* Free the input and output tokens */
|
---|
842 | Curl_safefree(ntlm->input_token);
|
---|
843 | Curl_safefree(ntlm->output_token);
|
---|
844 |
|
---|
845 | /* Reset any variables */
|
---|
846 | ntlm->token_max = 0;
|
---|
847 | }
|
---|
848 | #endif /* USE_NTLM */
|
---|
849 |
|
---|
850 | #if defined(USE_KERBEROS5)
|
---|
851 | /*
|
---|
852 | * Curl_sasl_create_gssapi_user_message()
|
---|
853 | *
|
---|
854 | * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
|
---|
855 | * message ready for sending to the recipient.
|
---|
856 | *
|
---|
857 | * Parameters:
|
---|
858 | *
|
---|
859 | * data [in] - The session handle.
|
---|
860 | * userp [in] - The user name in the format User or Domain\User.
|
---|
861 | * passdwp [in] - The user's password.
|
---|
862 | * service [in] - The service type such as www, smtp, pop or imap.
|
---|
863 | * mutual_auth [in] - Flag specifing whether or not mutual authentication
|
---|
864 | * is enabled.
|
---|
865 | * chlg64 [in] - The optional base64 encoded challenge message.
|
---|
866 | * krb5 [in/out] - The gssapi data struct being used and modified.
|
---|
867 | * outptr [in/out] - The address where a pointer to newly allocated memory
|
---|
868 | * holding the result will be stored upon completion.
|
---|
869 | * outlen [out] - The length of the output message.
|
---|
870 | *
|
---|
871 | * Returns CURLE_OK on success.
|
---|
872 | */
|
---|
873 | CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
|
---|
874 | const char *userp,
|
---|
875 | const char *passwdp,
|
---|
876 | const char *service,
|
---|
877 | const bool mutual_auth,
|
---|
878 | const char *chlg64,
|
---|
879 | struct kerberos5data *krb5,
|
---|
880 | char **outptr, size_t *outlen)
|
---|
881 | {
|
---|
882 | CURLcode result = CURLE_OK;
|
---|
883 | size_t chlglen = 0;
|
---|
884 | unsigned char *chlg = NULL;
|
---|
885 | CtxtHandle context;
|
---|
886 | PSecPkgInfo SecurityPackage;
|
---|
887 | SecBuffer chlg_buf;
|
---|
888 | SecBuffer resp_buf;
|
---|
889 | SecBufferDesc chlg_desc;
|
---|
890 | SecBufferDesc resp_desc;
|
---|
891 | SECURITY_STATUS status;
|
---|
892 | unsigned long attrs;
|
---|
893 | TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
|
---|
894 |
|
---|
895 | if(!krb5->credentials) {
|
---|
896 | /* Query the security package for Kerberos */
|
---|
897 | status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
|
---|
898 | TEXT(SP_NAME_KERBEROS),
|
---|
899 | &SecurityPackage);
|
---|
900 | if(status != SEC_E_OK) {
|
---|
901 | return CURLE_NOT_BUILT_IN;
|
---|
902 | }
|
---|
903 |
|
---|
904 | krb5->token_max = SecurityPackage->cbMaxToken;
|
---|
905 |
|
---|
906 | /* Release the package buffer as it is not required anymore */
|
---|
907 | s_pSecFn->FreeContextBuffer(SecurityPackage);
|
---|
908 |
|
---|
909 | /* Allocate our response buffer */
|
---|
910 | krb5->output_token = malloc(krb5->token_max);
|
---|
911 | if(!krb5->output_token)
|
---|
912 | return CURLE_OUT_OF_MEMORY;
|
---|
913 |
|
---|
914 | /* Generate our SPN */
|
---|
915 | krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
|
---|
916 | if(!krb5->spn)
|
---|
917 | return CURLE_OUT_OF_MEMORY;
|
---|
918 |
|
---|
919 | if(userp && *userp) {
|
---|
920 | /* Populate our identity structure */
|
---|
921 | result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
|
---|
922 | if(result)
|
---|
923 | return result;
|
---|
924 |
|
---|
925 | /* Allow proper cleanup of the identity structure */
|
---|
926 | krb5->p_identity = &krb5->identity;
|
---|
927 | }
|
---|
928 | else
|
---|
929 | /* Use the current Windows user */
|
---|
930 | krb5->p_identity = NULL;
|
---|
931 |
|
---|
932 | /* Allocate our credentials handle */
|
---|
933 | krb5->credentials = malloc(sizeof(CredHandle));
|
---|
934 | if(!krb5->credentials)
|
---|
935 | return CURLE_OUT_OF_MEMORY;
|
---|
936 |
|
---|
937 | memset(krb5->credentials, 0, sizeof(CredHandle));
|
---|
938 |
|
---|
939 | /* Acquire our credentials handle */
|
---|
940 | status = s_pSecFn->AcquireCredentialsHandle(NULL,
|
---|
941 | (TCHAR *)
|
---|
942 | TEXT(SP_NAME_KERBEROS),
|
---|
943 | SECPKG_CRED_OUTBOUND, NULL,
|
---|
944 | krb5->p_identity, NULL, NULL,
|
---|
945 | krb5->credentials, &expiry);
|
---|
946 | if(status != SEC_E_OK)
|
---|
947 | return CURLE_LOGIN_DENIED;
|
---|
948 |
|
---|
949 | /* Allocate our new context handle */
|
---|
950 | krb5->context = malloc(sizeof(CtxtHandle));
|
---|
951 | if(!krb5->context)
|
---|
952 | return CURLE_OUT_OF_MEMORY;
|
---|
953 |
|
---|
954 | memset(krb5->context, 0, sizeof(CtxtHandle));
|
---|
955 | }
|
---|
956 | else {
|
---|
957 | /* Decode the base-64 encoded challenge message */
|
---|
958 | if(strlen(chlg64) && *chlg64 != '=') {
|
---|
959 | result = Curl_base64_decode(chlg64, &chlg, &chlglen);
|
---|
960 | if(result)
|
---|
961 | return result;
|
---|
962 | }
|
---|
963 |
|
---|
964 | /* Ensure we have a valid challenge message */
|
---|
965 | if(!chlg) {
|
---|
966 | infof(data, "GSSAPI handshake failure (empty challenge message)\n");
|
---|
967 |
|
---|
968 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
969 | }
|
---|
970 |
|
---|
971 | /* Setup the challenge "input" security buffer */
|
---|
972 | chlg_desc.ulVersion = SECBUFFER_VERSION;
|
---|
973 | chlg_desc.cBuffers = 1;
|
---|
974 | chlg_desc.pBuffers = &chlg_buf;
|
---|
975 | chlg_buf.BufferType = SECBUFFER_TOKEN;
|
---|
976 | chlg_buf.pvBuffer = chlg;
|
---|
977 | chlg_buf.cbBuffer = curlx_uztoul(chlglen);
|
---|
978 | }
|
---|
979 |
|
---|
980 | /* Setup the response "output" security buffer */
|
---|
981 | resp_desc.ulVersion = SECBUFFER_VERSION;
|
---|
982 | resp_desc.cBuffers = 1;
|
---|
983 | resp_desc.pBuffers = &resp_buf;
|
---|
984 | resp_buf.BufferType = SECBUFFER_TOKEN;
|
---|
985 | resp_buf.pvBuffer = krb5->output_token;
|
---|
986 | resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
|
---|
987 |
|
---|
988 | /* Generate our challenge-response message */
|
---|
989 | status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
|
---|
990 | chlg ? krb5->context : NULL,
|
---|
991 | krb5->spn,
|
---|
992 | (mutual_auth ?
|
---|
993 | ISC_REQ_MUTUAL_AUTH : 0),
|
---|
994 | 0, SECURITY_NATIVE_DREP,
|
---|
995 | chlg ? &chlg_desc : NULL, 0,
|
---|
996 | &context,
|
---|
997 | &resp_desc, &attrs,
|
---|
998 | &expiry);
|
---|
999 |
|
---|
1000 | if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
|
---|
1001 | free(chlg);
|
---|
1002 |
|
---|
1003 | return CURLE_RECV_ERROR;
|
---|
1004 | }
|
---|
1005 |
|
---|
1006 | if(memcmp(&context, krb5->context, sizeof(context))) {
|
---|
1007 | s_pSecFn->DeleteSecurityContext(krb5->context);
|
---|
1008 |
|
---|
1009 | memcpy(krb5->context, &context, sizeof(context));
|
---|
1010 | }
|
---|
1011 |
|
---|
1012 | if(resp_buf.cbBuffer) {
|
---|
1013 | /* Base64 encode the response */
|
---|
1014 | result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer,
|
---|
1015 | resp_buf.cbBuffer, outptr, outlen);
|
---|
1016 | }
|
---|
1017 |
|
---|
1018 | /* Free the decoded challenge */
|
---|
1019 | free(chlg);
|
---|
1020 |
|
---|
1021 | return result;
|
---|
1022 | }
|
---|
1023 |
|
---|
1024 | /*
|
---|
1025 | * Curl_sasl_create_gssapi_security_message()
|
---|
1026 | *
|
---|
1027 | * This is used to generate an already encoded GSSAPI (Kerberos V5) security
|
---|
1028 | * token message ready for sending to the recipient.
|
---|
1029 | *
|
---|
1030 | * Parameters:
|
---|
1031 | *
|
---|
1032 | * data [in] - The session handle.
|
---|
1033 | * chlg64 [in] - The optional base64 encoded challenge message.
|
---|
1034 | * krb5 [in/out] - The gssapi data struct being used and modified.
|
---|
1035 | * outptr [in/out] - The address where a pointer to newly allocated memory
|
---|
1036 | * holding the result will be stored upon completion.
|
---|
1037 | * outlen [out] - The length of the output message.
|
---|
1038 | *
|
---|
1039 | * Returns CURLE_OK on success.
|
---|
1040 | */
|
---|
1041 | CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
|
---|
1042 | const char *chlg64,
|
---|
1043 | struct kerberos5data *krb5,
|
---|
1044 | char **outptr,
|
---|
1045 | size_t *outlen)
|
---|
1046 | {
|
---|
1047 | CURLcode result = CURLE_OK;
|
---|
1048 | size_t offset = 0;
|
---|
1049 | size_t chlglen = 0;
|
---|
1050 | size_t messagelen = 0;
|
---|
1051 | size_t appdatalen = 0;
|
---|
1052 | unsigned char *chlg = NULL;
|
---|
1053 | unsigned char *trailer = NULL;
|
---|
1054 | unsigned char *message = NULL;
|
---|
1055 | unsigned char *padding = NULL;
|
---|
1056 | unsigned char *appdata = NULL;
|
---|
1057 | SecBuffer input_buf[2];
|
---|
1058 | SecBuffer wrap_buf[3];
|
---|
1059 | SecBufferDesc input_desc;
|
---|
1060 | SecBufferDesc wrap_desc;
|
---|
1061 | unsigned long indata = 0;
|
---|
1062 | unsigned long outdata = 0;
|
---|
1063 | unsigned long qop = 0;
|
---|
1064 | unsigned long sec_layer = 0;
|
---|
1065 | unsigned long max_size = 0;
|
---|
1066 | SecPkgContext_Sizes sizes;
|
---|
1067 | SecPkgCredentials_Names names;
|
---|
1068 | SECURITY_STATUS status;
|
---|
1069 | char *user_name;
|
---|
1070 |
|
---|
1071 | /* Decode the base-64 encoded input message */
|
---|
1072 | if(strlen(chlg64) && *chlg64 != '=') {
|
---|
1073 | result = Curl_base64_decode(chlg64, &chlg, &chlglen);
|
---|
1074 | if(result)
|
---|
1075 | return result;
|
---|
1076 | }
|
---|
1077 |
|
---|
1078 | /* Ensure we have a valid challenge message */
|
---|
1079 | if(!chlg) {
|
---|
1080 | infof(data, "GSSAPI handshake failure (empty security message)\n");
|
---|
1081 |
|
---|
1082 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
1083 | }
|
---|
1084 |
|
---|
1085 | /* Get our response size information */
|
---|
1086 | status = s_pSecFn->QueryContextAttributes(krb5->context,
|
---|
1087 | SECPKG_ATTR_SIZES,
|
---|
1088 | &sizes);
|
---|
1089 | if(status != SEC_E_OK) {
|
---|
1090 | free(chlg);
|
---|
1091 |
|
---|
1092 | return CURLE_OUT_OF_MEMORY;
|
---|
1093 | }
|
---|
1094 |
|
---|
1095 | /* Get the fully qualified username back from the context */
|
---|
1096 | status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
|
---|
1097 | SECPKG_CRED_ATTR_NAMES,
|
---|
1098 | &names);
|
---|
1099 | if(status != SEC_E_OK) {
|
---|
1100 | free(chlg);
|
---|
1101 |
|
---|
1102 | return CURLE_RECV_ERROR;
|
---|
1103 | }
|
---|
1104 |
|
---|
1105 | /* Setup the "input" security buffer */
|
---|
1106 | input_desc.ulVersion = SECBUFFER_VERSION;
|
---|
1107 | input_desc.cBuffers = 2;
|
---|
1108 | input_desc.pBuffers = input_buf;
|
---|
1109 | input_buf[0].BufferType = SECBUFFER_STREAM;
|
---|
1110 | input_buf[0].pvBuffer = chlg;
|
---|
1111 | input_buf[0].cbBuffer = curlx_uztoul(chlglen);
|
---|
1112 | input_buf[1].BufferType = SECBUFFER_DATA;
|
---|
1113 | input_buf[1].pvBuffer = NULL;
|
---|
1114 | input_buf[1].cbBuffer = 0;
|
---|
1115 |
|
---|
1116 | /* Decrypt the inbound challenge and obtain the qop */
|
---|
1117 | status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
|
---|
1118 | if(status != SEC_E_OK) {
|
---|
1119 | infof(data, "GSSAPI handshake failure (empty security message)\n");
|
---|
1120 |
|
---|
1121 | free(chlg);
|
---|
1122 |
|
---|
1123 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
1124 | }
|
---|
1125 |
|
---|
1126 | /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
|
---|
1127 | if(input_buf[1].cbBuffer != 4) {
|
---|
1128 | infof(data, "GSSAPI handshake failure (invalid security data)\n");
|
---|
1129 |
|
---|
1130 | free(chlg);
|
---|
1131 |
|
---|
1132 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
1133 | }
|
---|
1134 |
|
---|
1135 | /* Copy the data out and free the challenge as it is not required anymore */
|
---|
1136 | memcpy(&indata, input_buf[1].pvBuffer, 4);
|
---|
1137 | s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
|
---|
1138 | free(chlg);
|
---|
1139 |
|
---|
1140 | /* Extract the security layer */
|
---|
1141 | sec_layer = indata & 0x000000FF;
|
---|
1142 | if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
|
---|
1143 | infof(data, "GSSAPI handshake failure (invalid security layer)\n");
|
---|
1144 |
|
---|
1145 | return CURLE_BAD_CONTENT_ENCODING;
|
---|
1146 | }
|
---|
1147 |
|
---|
1148 | /* Extract the maximum message size the server can receive */
|
---|
1149 | max_size = ntohl(indata & 0xFFFFFF00);
|
---|
1150 | if(max_size > 0) {
|
---|
1151 | /* The server has told us it supports a maximum receive buffer, however, as
|
---|
1152 | we don't require one unless we are encrypting data, we tell the server
|
---|
1153 | our receive buffer is zero. */
|
---|
1154 | max_size = 0;
|
---|
1155 | }
|
---|
1156 |
|
---|
1157 | /* Allocate the trailer */
|
---|
1158 | trailer = malloc(sizes.cbSecurityTrailer);
|
---|
1159 | if(!trailer)
|
---|
1160 | return CURLE_OUT_OF_MEMORY;
|
---|
1161 |
|
---|
1162 | /* Convert the user name to UTF8 when operating with Unicode */
|
---|
1163 | user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
|
---|
1164 | if(!user_name) {
|
---|
1165 | free(trailer);
|
---|
1166 |
|
---|
1167 | return CURLE_OUT_OF_MEMORY;
|
---|
1168 | }
|
---|
1169 |
|
---|
1170 | /* Allocate our message */
|
---|
1171 | messagelen = sizeof(outdata) + strlen(user_name) + 1;
|
---|
1172 | message = malloc(messagelen);
|
---|
1173 | if(!message) {
|
---|
1174 | free(trailer);
|
---|
1175 | Curl_unicodefree(user_name);
|
---|
1176 |
|
---|
1177 | return CURLE_OUT_OF_MEMORY;
|
---|
1178 | }
|
---|
1179 |
|
---|
1180 | /* Populate the message with the security layer, client supported receive
|
---|
1181 | message size and authorization identity including the 0x00 based
|
---|
1182 | terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization
|
---|
1183 | identity is not terminated with the zero-valued (%x00) octet." it seems
|
---|
1184 | necessary to include it. */
|
---|
1185 | outdata = htonl(max_size) | sec_layer;
|
---|
1186 | memcpy(message, &outdata, sizeof(outdata));
|
---|
1187 | strcpy((char *) message + sizeof(outdata), user_name);
|
---|
1188 | Curl_unicodefree(user_name);
|
---|
1189 |
|
---|
1190 | /* Allocate the padding */
|
---|
1191 | padding = malloc(sizes.cbBlockSize);
|
---|
1192 | if(!padding) {
|
---|
1193 | free(message);
|
---|
1194 | free(trailer);
|
---|
1195 |
|
---|
1196 | return CURLE_OUT_OF_MEMORY;
|
---|
1197 | }
|
---|
1198 |
|
---|
1199 | /* Setup the "authentication data" security buffer */
|
---|
1200 | wrap_desc.ulVersion = SECBUFFER_VERSION;
|
---|
1201 | wrap_desc.cBuffers = 3;
|
---|
1202 | wrap_desc.pBuffers = wrap_buf;
|
---|
1203 | wrap_buf[0].BufferType = SECBUFFER_TOKEN;
|
---|
1204 | wrap_buf[0].pvBuffer = trailer;
|
---|
1205 | wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
|
---|
1206 | wrap_buf[1].BufferType = SECBUFFER_DATA;
|
---|
1207 | wrap_buf[1].pvBuffer = message;
|
---|
1208 | wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
|
---|
1209 | wrap_buf[2].BufferType = SECBUFFER_PADDING;
|
---|
1210 | wrap_buf[2].pvBuffer = padding;
|
---|
1211 | wrap_buf[2].cbBuffer = sizes.cbBlockSize;
|
---|
1212 |
|
---|
1213 | /* Encrypt the data */
|
---|
1214 | status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
|
---|
1215 | &wrap_desc, 0);
|
---|
1216 | if(status != SEC_E_OK) {
|
---|
1217 | free(padding);
|
---|
1218 | free(message);
|
---|
1219 | free(trailer);
|
---|
1220 |
|
---|
1221 | return CURLE_OUT_OF_MEMORY;
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | /* Allocate the encryption (wrap) buffer */
|
---|
1225 | appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
|
---|
1226 | wrap_buf[2].cbBuffer;
|
---|
1227 | appdata = malloc(appdatalen);
|
---|
1228 | if(!appdata) {
|
---|
1229 | free(padding);
|
---|
1230 | free(message);
|
---|
1231 | free(trailer);
|
---|
1232 |
|
---|
1233 | return CURLE_OUT_OF_MEMORY;
|
---|
1234 | }
|
---|
1235 |
|
---|
1236 | /* Populate the encryption buffer */
|
---|
1237 | memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
|
---|
1238 | offset += wrap_buf[0].cbBuffer;
|
---|
1239 | memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
|
---|
1240 | offset += wrap_buf[1].cbBuffer;
|
---|
1241 | memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
|
---|
1242 |
|
---|
1243 | /* Base64 encode the response */
|
---|
1244 | result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
|
---|
1245 | outlen);
|
---|
1246 |
|
---|
1247 | /* Free all of our local buffers */
|
---|
1248 | free(appdata);
|
---|
1249 | free(padding);
|
---|
1250 | free(message);
|
---|
1251 | free(trailer);
|
---|
1252 |
|
---|
1253 | return result;
|
---|
1254 | }
|
---|
1255 |
|
---|
1256 | /*
|
---|
1257 | * Curl_sasl_gssapi_cleanup()
|
---|
1258 | *
|
---|
1259 | * This is used to clean up the gssapi specific data.
|
---|
1260 | *
|
---|
1261 | * Parameters:
|
---|
1262 | *
|
---|
1263 | * krb5 [in/out] - The kerberos 5 data struct being cleaned up.
|
---|
1264 | *
|
---|
1265 | */
|
---|
1266 | void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
|
---|
1267 | {
|
---|
1268 | /* Free our security context */
|
---|
1269 | if(krb5->context) {
|
---|
1270 | s_pSecFn->DeleteSecurityContext(krb5->context);
|
---|
1271 | free(krb5->context);
|
---|
1272 | krb5->context = NULL;
|
---|
1273 | }
|
---|
1274 |
|
---|
1275 | /* Free our credentials handle */
|
---|
1276 | if(krb5->credentials) {
|
---|
1277 | s_pSecFn->FreeCredentialsHandle(krb5->credentials);
|
---|
1278 | free(krb5->credentials);
|
---|
1279 | krb5->credentials = NULL;
|
---|
1280 | }
|
---|
1281 |
|
---|
1282 | /* Free our identity */
|
---|
1283 | Curl_sspi_free_identity(krb5->p_identity);
|
---|
1284 | krb5->p_identity = NULL;
|
---|
1285 |
|
---|
1286 | /* Free the SPN and output token */
|
---|
1287 | Curl_safefree(krb5->spn);
|
---|
1288 | Curl_safefree(krb5->output_token);
|
---|
1289 |
|
---|
1290 | /* Reset any variables */
|
---|
1291 | krb5->token_max = 0;
|
---|
1292 | }
|
---|
1293 | #endif /* USE_KERBEROS5 */
|
---|
1294 |
|
---|
1295 | #endif /* USE_WINDOWS_SSPI */
|
---|