1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2014 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) 2014, 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 http://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/host.
53 *
54 * Parameters:
55 *
56 * serivce [in] - The service type such as www, smtp, pop or imap.
57 * host [in] - The host name or realm.
58 *
59 * Returns a pointer to the newly allocated SPN.
60 */
Curl_sasl_build_spn(const char * service,const char * host)61 TCHAR *Curl_sasl_build_spn(const char *service, const char *host)
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, host);
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 */
Curl_sasl_create_digest_md5_message(struct SessionHandle * data,const char * chlg64,const char * userp,const char * passwdp,const char * service,char ** outptr,size_t * outlen)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 */
Curl_override_sspi_http_realm(const char * chlg,SEC_WINNT_AUTH_IDENTITY * identity)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 identity->Domain = dup_domain.tbyte_ptr;
320 identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
321 dup_domain.tchar_ptr = NULL;
322
323 Curl_unicodefree(domain.tchar_ptr);
324 }
325 else {
326 /* unknown specifier, ignore it! */
327 }
328 }
329 else
330 break; /* we're done here */
331
332 /* Pass all additional spaces here */
333 while(*chlg && ISSPACE(*chlg))
334 chlg++;
335
336 /* Allow the list to be comma-separated */
337 if(',' == *chlg)
338 chlg++;
339 }
340 }
341
342 return CURLE_OK;
343 }
344
345 /*
346 * Curl_sasl_decode_digest_http_message()
347 *
348 * This is used to decode a HTTP DIGEST challenge message into the seperate
349 * attributes.
350 *
351 * Parameters:
352 *
353 * chlg [in] - The challenge message.
354 * digest [in/out] - The digest data struct being used and modified.
355 *
356 * Returns CURLE_OK on success.
357 */
Curl_sasl_decode_digest_http_message(const char * chlg,struct digestdata * digest)358 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
359 struct digestdata *digest)
360 {
361 size_t chlglen = strlen(chlg);
362
363 /* We had an input token before and we got another one now. This means we
364 provided bad credentials in the previous request. */
365 if(digest->input_token)
366 return CURLE_BAD_CONTENT_ENCODING;
367
368 /* Simply store the challenge for use later */
369 digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
370 if(!digest->input_token)
371 return CURLE_OUT_OF_MEMORY;
372
373 digest->input_token_len = chlglen;
374
375 return CURLE_OK;
376 }
377
378 /*
379 * Curl_sasl_create_digest_http_message()
380 *
381 * This is used to generate a HTTP DIGEST response message ready for sending
382 * to the recipient.
383 *
384 * Parameters:
385 *
386 * data [in] - The session handle.
387 * userp [in] - The user name in the format User or Domain\User.
388 * passdwp [in] - The user's password.
389 * request [in] - The HTTP request.
390 * uripath [in] - The path of the HTTP uri.
391 * digest [in/out] - The digest data struct being used and modified.
392 * outptr [in/out] - The address where a pointer to newly allocated memory
393 * holding the result will be stored upon completion.
394 * outlen [out] - The length of the output message.
395 *
396 * Returns CURLE_OK on success.
397 */
Curl_sasl_create_digest_http_message(struct SessionHandle * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen)398 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
399 const char *userp,
400 const char *passwdp,
401 const unsigned char *request,
402 const unsigned char *uripath,
403 struct digestdata *digest,
404 char **outptr, size_t *outlen)
405 {
406 size_t token_max;
407 CredHandle credentials;
408 CtxtHandle context;
409 char *resp;
410 BYTE *output_token;
411 PSecPkgInfo SecurityPackage;
412 SEC_WINNT_AUTH_IDENTITY identity;
413 SEC_WINNT_AUTH_IDENTITY *p_identity;
414 SecBuffer chlg_buf[3];
415 SecBuffer resp_buf;
416 SecBufferDesc chlg_desc;
417 SecBufferDesc resp_desc;
418 SECURITY_STATUS status;
419 unsigned long attrs;
420 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
421
422 (void) data;
423
424 /* Query the security package for DigestSSP */
425 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
426 &SecurityPackage);
427 if(status != SEC_E_OK)
428 return CURLE_NOT_BUILT_IN;
429
430 token_max = SecurityPackage->cbMaxToken;
431
432 /* Release the package buffer as it is not required anymore */
433 s_pSecFn->FreeContextBuffer(SecurityPackage);
434
435 /* Allocate the output buffer according to the max token size as indicated
436 by the security package */
437 output_token = malloc(token_max);
438 if(!output_token)
439 return CURLE_OUT_OF_MEMORY;
440
441 if(userp && *userp) {
442 /* Populate our identity structure */
443 if(Curl_create_sspi_identity(userp, passwdp, &identity))
444 return CURLE_OUT_OF_MEMORY;
445
446 /* Populate our identity domain */
447 if(Curl_override_sspi_http_realm((const char*)digest->input_token,
448 &identity))
449 return CURLE_OUT_OF_MEMORY;
450
451 /* Allow proper cleanup of the identity structure */
452 p_identity = &identity;
453 }
454 else
455 /* Use the current Windows user */
456 p_identity = NULL;
457
458 /* Acquire our credentials handle */
459 status = s_pSecFn->AcquireCredentialsHandle(NULL,
460 (TCHAR *) TEXT(SP_NAME_DIGEST),
461 SECPKG_CRED_OUTBOUND, NULL,
462 p_identity, NULL, NULL,
463 &credentials, &expiry);
464 if(status != SEC_E_OK) {
465 free(output_token);
466
467 return CURLE_LOGIN_DENIED;
468 }
469
470 /* Setup the challenge "input" security buffer if present */
471 chlg_desc.ulVersion = SECBUFFER_VERSION;
472 chlg_desc.cBuffers = 3;
473 chlg_desc.pBuffers = chlg_buf;
474 chlg_buf[0].BufferType = SECBUFFER_TOKEN;
475 chlg_buf[0].pvBuffer = digest->input_token;
476 chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
477 chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
478 chlg_buf[1].pvBuffer = (void *)request;
479 chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
480 chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
481 chlg_buf[2].pvBuffer = NULL;
482 chlg_buf[2].cbBuffer = 0;
483
484 /* Setup the response "output" security buffer */
485 resp_desc.ulVersion = SECBUFFER_VERSION;
486 resp_desc.cBuffers = 1;
487 resp_desc.pBuffers = &resp_buf;
488 resp_buf.BufferType = SECBUFFER_TOKEN;
489 resp_buf.pvBuffer = output_token;
490 resp_buf.cbBuffer = curlx_uztoul(token_max);
491
492 /* Generate our reponse message */
493 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
494 (TCHAR *) uripath,
495 ISC_REQ_USE_HTTP_STYLE, 0, 0,
496 &chlg_desc, 0, &context,
497 &resp_desc, &attrs, &expiry);
498
499 if(status == SEC_I_COMPLETE_NEEDED ||
500 status == SEC_I_COMPLETE_AND_CONTINUE)
501 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
502 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
503 s_pSecFn->FreeCredentialsHandle(&credentials);
504
505 free(output_token);
506
507 return CURLE_OUT_OF_MEMORY;
508 }
509
510 resp = malloc(resp_buf.cbBuffer + 1);
511 if(!resp) {
512 s_pSecFn->DeleteSecurityContext(&context);
513 s_pSecFn->FreeCredentialsHandle(&credentials);
514
515 free(output_token);
516
517 return CURLE_OUT_OF_MEMORY;
518 }
519
520 /* Copy the generated reponse */
521 memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
522 resp[resp_buf.cbBuffer] = 0x00;
523
524 /* Return the response */
525 *outptr = resp;
526 *outlen = resp_buf.cbBuffer;
527
528 /* Free our handles */
529 s_pSecFn->DeleteSecurityContext(&context);
530 s_pSecFn->FreeCredentialsHandle(&credentials);
531
532 /* Free the identity structure */
533 Curl_sspi_free_identity(p_identity);
534
535 /* Free the response buffer */
536 free(output_token);
537
538 return CURLE_OK;
539 }
540
541 /*
542 * Curl_sasl_digest_cleanup()
543 *
544 * This is used to clean up the digest specific data.
545 *
546 * Parameters:
547 *
548 * digest [in/out] - The digest data struct being cleaned up.
549 *
550 */
Curl_sasl_digest_cleanup(struct digestdata * digest)551 void Curl_sasl_digest_cleanup(struct digestdata *digest)
552 {
553 /* Free the input token */
554 Curl_safefree(digest->input_token);
555
556 /* Reset any variables */
557 digest->input_token_len = 0;
558 }
559 #endif /* !CURL_DISABLE_CRYPTO_AUTH */
560
561 #if defined USE_NTLM
562 /*
563 * Curl_sasl_create_ntlm_type1_message()
564 *
565 * This is used to generate an already encoded NTLM type-1 message ready for
566 * sending to the recipient.
567 *
568 * Parameters:
569 *
570 * userp [in] - The user name in the format User or Domain\User.
571 * passdwp [in] - The user's password.
572 * ntlm [in/out] - The ntlm data struct being used and modified.
573 * outptr [in/out] - The address where a pointer to newly allocated memory
574 * holding the result will be stored upon completion.
575 * outlen [out] - The length of the output message.
576 *
577 * Returns CURLE_OK on success.
578 */
Curl_sasl_create_ntlm_type1_message(const char * userp,const char * passwdp,struct ntlmdata * ntlm,char ** outptr,size_t * outlen)579 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
580 const char *passwdp,
581 struct ntlmdata *ntlm,
582 char **outptr, size_t *outlen)
583 {
584 PSecPkgInfo SecurityPackage;
585 SecBuffer type_1_buf;
586 SecBufferDesc type_1_desc;
587 SECURITY_STATUS status;
588 unsigned long attrs;
589 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
590
591 /* Clean up any former leftovers and initialise to defaults */
592 Curl_sasl_ntlm_cleanup(ntlm);
593
594 /* Query the security package for NTLM */
595 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
596 &SecurityPackage);
597 if(status != SEC_E_OK)
598 return CURLE_NOT_BUILT_IN;
599
600 ntlm->token_max = SecurityPackage->cbMaxToken;
601
602 /* Release the package buffer as it is not required anymore */
603 s_pSecFn->FreeContextBuffer(SecurityPackage);
604
605 /* Allocate our output buffer */
606 ntlm->output_token = malloc(ntlm->token_max);
607 if(!ntlm->output_token)
608 return CURLE_OUT_OF_MEMORY;
609
610 if(userp && *userp) {
611 CURLcode result;
612
613 /* Populate our identity structure */
614 result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
615 if(result)
616 return result;
617
618 /* Allow proper cleanup of the identity structure */
619 ntlm->p_identity = &ntlm->identity;
620 }
621 else
622 /* Use the current Windows user */
623 ntlm->p_identity = NULL;
624
625 /* Allocate our credentials handle */
626 ntlm->credentials = malloc(sizeof(CredHandle));
627 if(!ntlm->credentials)
628 return CURLE_OUT_OF_MEMORY;
629
630 memset(ntlm->credentials, 0, sizeof(CredHandle));
631
632 /* Acquire our credentials handle */
633 status = s_pSecFn->AcquireCredentialsHandle(NULL,
634 (TCHAR *) TEXT(SP_NAME_NTLM),
635 SECPKG_CRED_OUTBOUND, NULL,
636 ntlm->p_identity, NULL, NULL,
637 ntlm->credentials, &expiry);
638 if(status != SEC_E_OK)
639 return CURLE_LOGIN_DENIED;
640
641 /* Allocate our new context handle */
642 ntlm->context = malloc(sizeof(CtxtHandle));
643 if(!ntlm->context)
644 return CURLE_OUT_OF_MEMORY;
645
646 memset(ntlm->context, 0, sizeof(CtxtHandle));
647
648 /* Setup the type-1 "output" security buffer */
649 type_1_desc.ulVersion = SECBUFFER_VERSION;
650 type_1_desc.cBuffers = 1;
651 type_1_desc.pBuffers = &type_1_buf;
652 type_1_buf.BufferType = SECBUFFER_TOKEN;
653 type_1_buf.pvBuffer = ntlm->output_token;
654 type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
655
656 /* Generate our type-1 message */
657 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
658 (TCHAR *) TEXT(""),
659 0, 0, SECURITY_NETWORK_DREP,
660 NULL, 0,
661 ntlm->context, &type_1_desc,
662 &attrs, &expiry);
663 if(status == SEC_I_COMPLETE_NEEDED ||
664 status == SEC_I_COMPLETE_AND_CONTINUE)
665 s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
666 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
667 return CURLE_RECV_ERROR;
668
669 /* Base64 encode the response */
670 return Curl_base64_encode(NULL, (char *) ntlm->output_token,
671 type_1_buf.cbBuffer, outptr, outlen);
672 }
673
674 /*
675 * Curl_sasl_decode_ntlm_type2_message()
676 *
677 * This is used to decode an already encoded NTLM type-2 message.
678 *
679 * Parameters:
680 *
681 * data [in] - The session handle.
682 * type2msg [in] - The base64 encoded type-2 message.
683 * ntlm [in/out] - The ntlm data struct being used and modified.
684 *
685 * Returns CURLE_OK on success.
686 */
Curl_sasl_decode_ntlm_type2_message(struct SessionHandle * data,const char * type2msg,struct ntlmdata * ntlm)687 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
688 const char *type2msg,
689 struct ntlmdata *ntlm)
690 {
691 CURLcode result = CURLE_OK;
692 unsigned char *type2 = NULL;
693 size_t type2_len = 0;
694
695 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
696 (void) data;
697 #endif
698
699 /* Decode the base-64 encoded type-2 message */
700 if(strlen(type2msg) && *type2msg != '=') {
701 result = Curl_base64_decode(type2msg, &type2, &type2_len);
702 if(result)
703 return result;
704 }
705
706 /* Ensure we have a valid type-2 message */
707 if(!type2) {
708 infof(data, "NTLM handshake failure (empty type-2 message)\n");
709
710 return CURLE_BAD_CONTENT_ENCODING;
711 }
712
713 /* Simply store the challenge for use later */
714 ntlm->input_token = type2;
715 ntlm->input_token_len = type2_len;
716
717 return result;
718 }
719
720 /*
721 * Curl_sasl_create_ntlm_type3_message()
722 *
723 * This is used to generate an already encoded NTLM type-3 message ready for
724 * sending to the recipient.
725 *
726 * Parameters:
727 *
728 * data [in] - The session handle.
729 * userp [in] - The user name in the format User or Domain\User.
730 * passdwp [in] - The user's password.
731 * ntlm [in/out] - The ntlm data struct being used and modified.
732 * outptr [in/out] - The address where a pointer to newly allocated memory
733 * holding the result will be stored upon completion.
734 * outlen [out] - The length of the output message.
735 *
736 * Returns CURLE_OK on success.
737 */
Curl_sasl_create_ntlm_type3_message(struct SessionHandle * data,const char * userp,const char * passwdp,struct ntlmdata * ntlm,char ** outptr,size_t * outlen)738 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
739 const char *userp,
740 const char *passwdp,
741 struct ntlmdata *ntlm,
742 char **outptr, size_t *outlen)
743 {
744 CURLcode result = CURLE_OK;
745 SecBuffer type_2_buf;
746 SecBuffer type_3_buf;
747 SecBufferDesc type_2_desc;
748 SecBufferDesc type_3_desc;
749 SECURITY_STATUS status;
750 unsigned long attrs;
751 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
752
753 (void) passwdp;
754 (void) userp;
755
756 /* Setup the type-2 "input" security buffer */
757 type_2_desc.ulVersion = SECBUFFER_VERSION;
758 type_2_desc.cBuffers = 1;
759 type_2_desc.pBuffers = &type_2_buf;
760 type_2_buf.BufferType = SECBUFFER_TOKEN;
761 type_2_buf.pvBuffer = ntlm->input_token;
762 type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len);
763
764 /* Setup the type-3 "output" security buffer */
765 type_3_desc.ulVersion = SECBUFFER_VERSION;
766 type_3_desc.cBuffers = 1;
767 type_3_desc.pBuffers = &type_3_buf;
768 type_3_buf.BufferType = SECBUFFER_TOKEN;
769 type_3_buf.pvBuffer = ntlm->output_token;
770 type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
771
772 /* Generate our type-3 message */
773 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
774 ntlm->context,
775 (TCHAR *) TEXT(""),
776 0, 0, SECURITY_NETWORK_DREP,
777 &type_2_desc,
778 0, ntlm->context,
779 &type_3_desc,
780 &attrs, &expiry);
781 if(status != SEC_E_OK) {
782 infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
783 status);
784
785 return CURLE_RECV_ERROR;
786 }
787
788 /* Base64 encode the response */
789 result = Curl_base64_encode(data, (char *) ntlm->output_token,
790 type_3_buf.cbBuffer, outptr, outlen);
791
792 Curl_sasl_ntlm_cleanup(ntlm);
793
794 return result;
795 }
796
797 /*
798 * Curl_sasl_ntlm_cleanup()
799 *
800 * This is used to clean up the ntlm specific data.
801 *
802 * Parameters:
803 *
804 * ntlm [in/out] - The ntlm data struct being cleaned up.
805 *
806 */
Curl_sasl_ntlm_cleanup(struct ntlmdata * ntlm)807 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
808 {
809 /* Free our security context */
810 if(ntlm->context) {
811 s_pSecFn->DeleteSecurityContext(ntlm->context);
812 free(ntlm->context);
813 ntlm->context = NULL;
814 }
815
816 /* Free our credentials handle */
817 if(ntlm->credentials) {
818 s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
819 free(ntlm->credentials);
820 ntlm->credentials = NULL;
821 }
822
823 /* Free our identity */
824 Curl_sspi_free_identity(ntlm->p_identity);
825 ntlm->p_identity = NULL;
826
827 /* Free the input and output tokens */
828 Curl_safefree(ntlm->input_token);
829 Curl_safefree(ntlm->output_token);
830
831 /* Reset any variables */
832 ntlm->token_max = 0;
833 }
834 #endif /* USE_NTLM */
835
836 #if defined(USE_KERBEROS5)
837 /*
838 * Curl_sasl_create_gssapi_user_message()
839 *
840 * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
841 * message ready for sending to the recipient.
842 *
843 * Parameters:
844 *
845 * data [in] - The session handle.
846 * userp [in] - The user name in the format User or Domain\User.
847 * passdwp [in] - The user's password.
848 * service [in] - The service type such as www, smtp, pop or imap.
849 * mutual_auth [in] - Flag specifing whether or not mutual authentication
850 * is enabled.
851 * chlg64 [in] - The optional base64 encoded challenge message.
852 * krb5 [in/out] - The gssapi data struct being used and modified.
853 * outptr [in/out] - The address where a pointer to newly allocated memory
854 * holding the result will be stored upon completion.
855 * outlen [out] - The length of the output message.
856 *
857 * Returns CURLE_OK on success.
858 */
Curl_sasl_create_gssapi_user_message(struct SessionHandle * data,const char * userp,const char * passwdp,const char * service,const bool mutual_auth,const char * chlg64,struct kerberos5data * krb5,char ** outptr,size_t * outlen)859 CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
860 const char *userp,
861 const char *passwdp,
862 const char *service,
863 const bool mutual_auth,
864 const char *chlg64,
865 struct kerberos5data *krb5,
866 char **outptr, size_t *outlen)
867 {
868 CURLcode result = CURLE_OK;
869 size_t chlglen = 0;
870 unsigned char *chlg = NULL;
871 CtxtHandle context;
872 PSecPkgInfo SecurityPackage;
873 SecBuffer chlg_buf;
874 SecBuffer resp_buf;
875 SecBufferDesc chlg_desc;
876 SecBufferDesc resp_desc;
877 SECURITY_STATUS status;
878 unsigned long attrs;
879 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
880
881 if(!krb5->credentials) {
882 /* Query the security package for Kerberos */
883 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
884 TEXT(SP_NAME_KERBEROS),
885 &SecurityPackage);
886 if(status != SEC_E_OK) {
887 return CURLE_NOT_BUILT_IN;
888 }
889
890 krb5->token_max = SecurityPackage->cbMaxToken;
891
892 /* Release the package buffer as it is not required anymore */
893 s_pSecFn->FreeContextBuffer(SecurityPackage);
894
895 /* Allocate our response buffer */
896 krb5->output_token = malloc(krb5->token_max);
897 if(!krb5->output_token)
898 return CURLE_OUT_OF_MEMORY;
899
900 /* Generate our SPN */
901 krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
902 if(!krb5->spn)
903 return CURLE_OUT_OF_MEMORY;
904
905 if(userp && *userp) {
906 /* Populate our identity structure */
907 result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
908 if(result)
909 return result;
910
911 /* Allow proper cleanup of the identity structure */
912 krb5->p_identity = &krb5->identity;
913 }
914 else
915 /* Use the current Windows user */
916 krb5->p_identity = NULL;
917
918 /* Allocate our credentials handle */
919 krb5->credentials = malloc(sizeof(CredHandle));
920 if(!krb5->credentials)
921 return CURLE_OUT_OF_MEMORY;
922
923 memset(krb5->credentials, 0, sizeof(CredHandle));
924
925 /* Acquire our credentials handle */
926 status = s_pSecFn->AcquireCredentialsHandle(NULL,
927 (TCHAR *)
928 TEXT(SP_NAME_KERBEROS),
929 SECPKG_CRED_OUTBOUND, NULL,
930 krb5->p_identity, NULL, NULL,
931 krb5->credentials, &expiry);
932 if(status != SEC_E_OK)
933 return CURLE_LOGIN_DENIED;
934
935 /* Allocate our new context handle */
936 krb5->context = malloc(sizeof(CtxtHandle));
937 if(!krb5->context)
938 return CURLE_OUT_OF_MEMORY;
939
940 memset(krb5->context, 0, sizeof(CtxtHandle));
941 }
942 else {
943 /* Decode the base-64 encoded challenge message */
944 if(strlen(chlg64) && *chlg64 != '=') {
945 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
946 if(result)
947 return result;
948 }
949
950 /* Ensure we have a valid challenge message */
951 if(!chlg) {
952 infof(data, "GSSAPI handshake failure (empty challenge message)\n");
953
954 return CURLE_BAD_CONTENT_ENCODING;
955 }
956
957 /* Setup the challenge "input" security buffer */
958 chlg_desc.ulVersion = SECBUFFER_VERSION;
959 chlg_desc.cBuffers = 1;
960 chlg_desc.pBuffers = &chlg_buf;
961 chlg_buf.BufferType = SECBUFFER_TOKEN;
962 chlg_buf.pvBuffer = chlg;
963 chlg_buf.cbBuffer = curlx_uztoul(chlglen);
964 }
965
966 /* Setup the response "output" security buffer */
967 resp_desc.ulVersion = SECBUFFER_VERSION;
968 resp_desc.cBuffers = 1;
969 resp_desc.pBuffers = &resp_buf;
970 resp_buf.BufferType = SECBUFFER_TOKEN;
971 resp_buf.pvBuffer = krb5->output_token;
972 resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
973
974 /* Generate our challenge-response message */
975 status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
976 chlg ? krb5->context : NULL,
977 krb5->spn,
978 (mutual_auth ?
979 ISC_REQ_MUTUAL_AUTH : 0),
980 0, SECURITY_NATIVE_DREP,
981 chlg ? &chlg_desc : NULL, 0,
982 &context,
983 &resp_desc, &attrs,
984 &expiry);
985
986 if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
987 free(chlg);
988
989 return CURLE_RECV_ERROR;
990 }
991
992 if(memcmp(&context, krb5->context, sizeof(context))) {
993 s_pSecFn->DeleteSecurityContext(krb5->context);
994
995 memcpy(krb5->context, &context, sizeof(context));
996 }
997
998 if(resp_buf.cbBuffer) {
999 /* Base64 encode the response */
1000 result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer,
1001 resp_buf.cbBuffer, outptr, outlen);
1002 }
1003
1004 /* Free the decoded challenge */
1005 free(chlg);
1006
1007 return result;
1008 }
1009
1010 /*
1011 * Curl_sasl_create_gssapi_security_message()
1012 *
1013 * This is used to generate an already encoded GSSAPI (Kerberos V5) security
1014 * token message ready for sending to the recipient.
1015 *
1016 * Parameters:
1017 *
1018 * data [in] - The session handle.
1019 * chlg64 [in] - The optional base64 encoded challenge message.
1020 * krb5 [in/out] - The gssapi data struct being used and modified.
1021 * outptr [in/out] - The address where a pointer to newly allocated memory
1022 * holding the result will be stored upon completion.
1023 * outlen [out] - The length of the output message.
1024 *
1025 * Returns CURLE_OK on success.
1026 */
Curl_sasl_create_gssapi_security_message(struct SessionHandle * data,const char * chlg64,struct kerberos5data * krb5,char ** outptr,size_t * outlen)1027 CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
1028 const char *chlg64,
1029 struct kerberos5data *krb5,
1030 char **outptr,
1031 size_t *outlen)
1032 {
1033 CURLcode result = CURLE_OK;
1034 size_t offset = 0;
1035 size_t chlglen = 0;
1036 size_t messagelen = 0;
1037 size_t appdatalen = 0;
1038 unsigned char *chlg = NULL;
1039 unsigned char *trailer = NULL;
1040 unsigned char *message = NULL;
1041 unsigned char *padding = NULL;
1042 unsigned char *appdata = NULL;
1043 SecBuffer input_buf[2];
1044 SecBuffer wrap_buf[3];
1045 SecBufferDesc input_desc;
1046 SecBufferDesc wrap_desc;
1047 unsigned long indata = 0;
1048 unsigned long outdata = 0;
1049 unsigned long qop = 0;
1050 unsigned long sec_layer = 0;
1051 unsigned long max_size = 0;
1052 SecPkgContext_Sizes sizes;
1053 SecPkgCredentials_Names names;
1054 SECURITY_STATUS status;
1055 char *user_name;
1056
1057 /* Decode the base-64 encoded input message */
1058 if(strlen(chlg64) && *chlg64 != '=') {
1059 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
1060 if(result)
1061 return result;
1062 }
1063
1064 /* Ensure we have a valid challenge message */
1065 if(!chlg) {
1066 infof(data, "GSSAPI handshake failure (empty security message)\n");
1067
1068 return CURLE_BAD_CONTENT_ENCODING;
1069 }
1070
1071 /* Get our response size information */
1072 status = s_pSecFn->QueryContextAttributes(krb5->context,
1073 SECPKG_ATTR_SIZES,
1074 &sizes);
1075 if(status != SEC_E_OK) {
1076 free(chlg);
1077
1078 return CURLE_OUT_OF_MEMORY;
1079 }
1080
1081 /* Get the fully qualified username back from the context */
1082 status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
1083 SECPKG_CRED_ATTR_NAMES,
1084 &names);
1085 if(status != SEC_E_OK) {
1086 free(chlg);
1087
1088 return CURLE_RECV_ERROR;
1089 }
1090
1091 /* Setup the "input" security buffer */
1092 input_desc.ulVersion = SECBUFFER_VERSION;
1093 input_desc.cBuffers = 2;
1094 input_desc.pBuffers = input_buf;
1095 input_buf[0].BufferType = SECBUFFER_STREAM;
1096 input_buf[0].pvBuffer = chlg;
1097 input_buf[0].cbBuffer = curlx_uztoul(chlglen);
1098 input_buf[1].BufferType = SECBUFFER_DATA;
1099 input_buf[1].pvBuffer = NULL;
1100 input_buf[1].cbBuffer = 0;
1101
1102 /* Decrypt the inbound challenge and obtain the qop */
1103 status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
1104 if(status != SEC_E_OK) {
1105 infof(data, "GSSAPI handshake failure (empty security message)\n");
1106
1107 free(chlg);
1108
1109 return CURLE_BAD_CONTENT_ENCODING;
1110 }
1111
1112 /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
1113 if(input_buf[1].cbBuffer != 4) {
1114 infof(data, "GSSAPI handshake failure (invalid security data)\n");
1115
1116 free(chlg);
1117
1118 return CURLE_BAD_CONTENT_ENCODING;
1119 }
1120
1121 /* Copy the data out and free the challenge as it is not required anymore */
1122 memcpy(&indata, input_buf[1].pvBuffer, 4);
1123 s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
1124 free(chlg);
1125
1126 /* Extract the security layer */
1127 sec_layer = indata & 0x000000FF;
1128 if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
1129 infof(data, "GSSAPI handshake failure (invalid security layer)\n");
1130
1131 return CURLE_BAD_CONTENT_ENCODING;
1132 }
1133
1134 /* Extract the maximum message size the server can receive */
1135 max_size = ntohl(indata & 0xFFFFFF00);
1136 if(max_size > 0) {
1137 /* The server has told us it supports a maximum receive buffer, however, as
1138 we don't require one unless we are encrypting data, we tell the server
1139 our receive buffer is zero. */
1140 max_size = 0;
1141 }
1142
1143 /* Allocate the trailer */
1144 trailer = malloc(sizes.cbSecurityTrailer);
1145 if(!trailer)
1146 return CURLE_OUT_OF_MEMORY;
1147
1148 /* Convert the user name to UTF8 when operating with Unicode */
1149 user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
1150 if(!user_name) {
1151 free(trailer);
1152
1153 return CURLE_OUT_OF_MEMORY;
1154 }
1155
1156 /* Allocate our message */
1157 messagelen = sizeof(outdata) + strlen(user_name) + 1;
1158 message = malloc(messagelen);
1159 if(!message) {
1160 free(trailer);
1161 Curl_unicodefree(user_name);
1162
1163 return CURLE_OUT_OF_MEMORY;
1164 }
1165
1166 /* Populate the message with the security layer, client supported receive
1167 message size and authorization identity including the 0x00 based
1168 terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization
1169 identity is not terminated with the zero-valued (%x00) octet." it seems
1170 necessary to include it. */
1171 outdata = htonl(max_size) | sec_layer;
1172 memcpy(message, &outdata, sizeof(outdata));
1173 strcpy((char *) message + sizeof(outdata), user_name);
1174 Curl_unicodefree(user_name);
1175
1176 /* Allocate the padding */
1177 padding = malloc(sizes.cbBlockSize);
1178 if(!padding) {
1179 free(message);
1180 free(trailer);
1181
1182 return CURLE_OUT_OF_MEMORY;
1183 }
1184
1185 /* Setup the "authentication data" security buffer */
1186 wrap_desc.ulVersion = SECBUFFER_VERSION;
1187 wrap_desc.cBuffers = 3;
1188 wrap_desc.pBuffers = wrap_buf;
1189 wrap_buf[0].BufferType = SECBUFFER_TOKEN;
1190 wrap_buf[0].pvBuffer = trailer;
1191 wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
1192 wrap_buf[1].BufferType = SECBUFFER_DATA;
1193 wrap_buf[1].pvBuffer = message;
1194 wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
1195 wrap_buf[2].BufferType = SECBUFFER_PADDING;
1196 wrap_buf[2].pvBuffer = padding;
1197 wrap_buf[2].cbBuffer = sizes.cbBlockSize;
1198
1199 /* Encrypt the data */
1200 status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
1201 &wrap_desc, 0);
1202 if(status != SEC_E_OK) {
1203 free(padding);
1204 free(message);
1205 free(trailer);
1206
1207 return CURLE_OUT_OF_MEMORY;
1208 }
1209
1210 /* Allocate the encryption (wrap) buffer */
1211 appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
1212 wrap_buf[2].cbBuffer;
1213 appdata = malloc(appdatalen);
1214 if(!appdata) {
1215 free(padding);
1216 free(message);
1217 free(trailer);
1218
1219 return CURLE_OUT_OF_MEMORY;
1220 }
1221
1222 /* Populate the encryption buffer */
1223 memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
1224 offset += wrap_buf[0].cbBuffer;
1225 memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
1226 offset += wrap_buf[1].cbBuffer;
1227 memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
1228
1229 /* Base64 encode the response */
1230 result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
1231 outlen);
1232
1233 /* Free all of our local buffers */
1234 free(appdata);
1235 free(padding);
1236 free(message);
1237 free(trailer);
1238
1239 return result;
1240 }
1241
1242 /*
1243 * Curl_sasl_gssapi_cleanup()
1244 *
1245 * This is used to clean up the gssapi specific data.
1246 *
1247 * Parameters:
1248 *
1249 * krb5 [in/out] - The kerberos 5 data struct being cleaned up.
1250 *
1251 */
Curl_sasl_gssapi_cleanup(struct kerberos5data * krb5)1252 void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
1253 {
1254 /* Free our security context */
1255 if(krb5->context) {
1256 s_pSecFn->DeleteSecurityContext(krb5->context);
1257 free(krb5->context);
1258 krb5->context = NULL;
1259 }
1260
1261 /* Free our credentials handle */
1262 if(krb5->credentials) {
1263 s_pSecFn->FreeCredentialsHandle(krb5->credentials);
1264 free(krb5->credentials);
1265 krb5->credentials = NULL;
1266 }
1267
1268 /* Free our identity */
1269 Curl_sspi_free_identity(krb5->p_identity);
1270 krb5->p_identity = NULL;
1271
1272 /* Free the SPN and output token */
1273 Curl_safefree(krb5->spn);
1274 Curl_safefree(krb5->output_token);
1275
1276 /* Reset any variables */
1277 krb5->token_max = 0;
1278 }
1279 #endif /* USE_KERBEROS5 */
1280
1281 #endif /* USE_WINDOWS_SSPI */
1282