1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2014 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) 2014, Steve Holme, <steve_holme@hotmail.com>.
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.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
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
27 ***************************************************************************/
29 #include "curl_setup.h"
31 #if defined(USE_WINDOWS_SSPI)
33 #include <curl/curl.h>
35 #include "curl_sasl.h"
37 #include "curl_base64.h"
39 #include "curl_multibyte.h"
42 #include "curl_printf.h"
45 /* The last #include files should be: */
46 #include "curl_memory.h"
50 * Curl_sasl_build_spn()
52 * This is used to build a SPN string in the format service/host.
56 * serivce [in] - The service type such as www, smtp, pop or imap.
57 * host [in] - The host name or realm.
59 * Returns a pointer to the newly allocated SPN.
61 TCHAR *Curl_sasl_build_spn(const char *service, const char *host)
63 char *utf8_spn = NULL;
64 TCHAR *tchar_spn = NULL;
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. */
73 /* Allocate our UTF8 based SPN */
74 utf8_spn = aprintf("%s/%s", service, host);
79 /* Allocate our TCHAR based SPN */
80 tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn);
87 /* Release the UTF8 variant when operating with Unicode */
88 Curl_unicodefree(utf8_spn);
90 /* Return our newly allocated SPN */
94 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
96 * Curl_sasl_create_digest_md5_message()
98 * This is used to generate an already encoded DIGEST-MD5 response message
99 * ready for sending to the recipient.
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.
112 * Returns CURLE_OK on success.
114 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
119 char **outptr, size_t *outlen)
121 CURLcode result = CURLE_OK;
124 size_t token_max = 0;
125 unsigned char *input_token = NULL;
126 unsigned char *output_token = NULL;
127 CredHandle credentials;
129 PSecPkgInfo SecurityPackage;
130 SEC_WINNT_AUTH_IDENTITY identity;
131 SEC_WINNT_AUTH_IDENTITY *p_identity;
134 SecBufferDesc chlg_desc;
135 SecBufferDesc resp_desc;
136 SECURITY_STATUS status;
138 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
140 /* Decode the base-64 encoded challenge message */
141 if(strlen(chlg64) && *chlg64 != '=') {
142 result = Curl_base64_decode(chlg64, &input_token, &chlglen);
147 /* Ensure we have a valid challenge message */
149 infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
151 return CURLE_BAD_CONTENT_ENCODING;
154 /* Query the security package for DigestSSP */
155 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
157 if(status != SEC_E_OK) {
160 return CURLE_NOT_BUILT_IN;
163 token_max = SecurityPackage->cbMaxToken;
165 /* Release the package buffer as it is not required anymore */
166 s_pSecFn->FreeContextBuffer(SecurityPackage);
168 /* Allocate our response buffer */
169 output_token = malloc(token_max);
173 return CURLE_OUT_OF_MEMORY;
176 /* Generate our SPN */
177 spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
182 return CURLE_OUT_OF_MEMORY;
185 if(userp && *userp) {
186 /* Populate our identity structure */
187 result = Curl_create_sspi_identity(userp, passwdp, &identity);
196 /* Allow proper cleanup of the identity structure */
197 p_identity = &identity;
200 /* Use the current Windows user */
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);
210 if(status != SEC_E_OK) {
211 Curl_sspi_free_identity(p_identity);
216 return CURLE_LOGIN_DENIED;
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);
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);
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,
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);
251 return CURLE_RECV_ERROR;
254 /* Base64 encode the response */
255 result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
258 /* Free our handles */
259 s_pSecFn->DeleteSecurityContext(&context);
260 s_pSecFn->FreeCredentialsHandle(&credentials);
262 /* Free the identity structure */
263 Curl_sspi_free_identity(p_identity);
268 /* Free the response buffer */
271 /* Free the decoded challenge message */
278 * Curl_override_sspi_http_realm()
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.
286 * chlg [in] - The challenge message.
287 * identity [in/out] - The identity structure.
289 * Returns CURLE_OK on success.
291 CURLcode Curl_override_sspi_http_realm(const char *chlg,
292 SEC_WINNT_AUTH_IDENTITY *identity)
294 xcharp_u domain, dup_domain;
296 /* If domain is blank or unset, check challenge message for realm */
297 if(!identity->Domain || !identity->DomainLength) {
299 char value[DIGEST_MAX_VALUE_LENGTH];
300 char content[DIGEST_MAX_CONTENT_LENGTH];
302 /* Pass all additional spaces here */
303 while(*chlg && ISSPACE(*chlg))
306 /* Extract a value=content pair */
307 if(!Curl_sasl_digest_get_pair(chlg, value, content, &chlg)) {
308 if(Curl_raw_equal(value, "realm")) {
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;
319 identity->Domain = dup_domain.tbyte_ptr;
320 identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
321 dup_domain.tchar_ptr = NULL;
323 Curl_unicodefree(domain.tchar_ptr);
326 /* unknown specifier, ignore it! */
330 break; /* we're done here */
332 /* Pass all additional spaces here */
333 while(*chlg && ISSPACE(*chlg))
336 /* Allow the list to be comma-separated */
346 * Curl_sasl_decode_digest_http_message()
348 * This is used to decode a HTTP DIGEST challenge message into the seperate
353 * chlg [in] - The challenge message.
354 * digest [in/out] - The digest data struct being used and modified.
356 * Returns CURLE_OK on success.
358 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
359 struct digestdata *digest)
361 size_t chlglen = strlen(chlg);
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;
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;
373 digest->input_token_len = chlglen;
379 * Curl_sasl_create_digest_http_message()
381 * This is used to generate a HTTP DIGEST response message ready for sending
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.
396 * Returns CURLE_OK on success.
398 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
401 const unsigned char *request,
402 const unsigned char *uripath,
403 struct digestdata *digest,
404 char **outptr, size_t *outlen)
407 CredHandle credentials;
411 PSecPkgInfo SecurityPackage;
412 SEC_WINNT_AUTH_IDENTITY identity;
413 SEC_WINNT_AUTH_IDENTITY *p_identity;
414 SecBuffer chlg_buf[3];
416 SecBufferDesc chlg_desc;
417 SecBufferDesc resp_desc;
418 SECURITY_STATUS status;
420 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
424 /* Query the security package for DigestSSP */
425 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
427 if(status != SEC_E_OK)
428 return CURLE_NOT_BUILT_IN;
430 token_max = SecurityPackage->cbMaxToken;
432 /* Release the package buffer as it is not required anymore */
433 s_pSecFn->FreeContextBuffer(SecurityPackage);
435 /* Allocate the output buffer according to the max token size as indicated
436 by the security package */
437 output_token = malloc(token_max);
439 return CURLE_OUT_OF_MEMORY;
441 if(userp && *userp) {
442 /* Populate our identity structure */
443 if(Curl_create_sspi_identity(userp, passwdp, &identity))
444 return CURLE_OUT_OF_MEMORY;
446 /* Populate our identity domain */
447 if(Curl_override_sspi_http_realm((const char*)digest->input_token,
449 return CURLE_OUT_OF_MEMORY;
451 /* Allow proper cleanup of the identity structure */
452 p_identity = &identity;
455 /* Use the current Windows user */
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) {
467 return CURLE_LOGIN_DENIED;
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;
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);
492 /* Generate our reponse message */
493 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
495 ISC_REQ_USE_HTTP_STYLE, 0, 0,
496 &chlg_desc, 0, &context,
497 &resp_desc, &attrs, &expiry);
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);
507 return CURLE_OUT_OF_MEMORY;
510 resp = malloc(resp_buf.cbBuffer + 1);
512 s_pSecFn->DeleteSecurityContext(&context);
513 s_pSecFn->FreeCredentialsHandle(&credentials);
517 return CURLE_OUT_OF_MEMORY;
520 /* Copy the generated reponse */
521 memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
522 resp[resp_buf.cbBuffer] = 0x00;
524 /* Return the response */
526 *outlen = resp_buf.cbBuffer;
528 /* Free our handles */
529 s_pSecFn->DeleteSecurityContext(&context);
530 s_pSecFn->FreeCredentialsHandle(&credentials);
532 /* Free the identity structure */
533 Curl_sspi_free_identity(p_identity);
535 /* Free the response buffer */
542 * Curl_sasl_digest_cleanup()
544 * This is used to clean up the digest specific data.
548 * digest [in/out] - The digest data struct being cleaned up.
551 void Curl_sasl_digest_cleanup(struct digestdata *digest)
553 /* Free the input token */
554 Curl_safefree(digest->input_token);
556 /* Reset any variables */
557 digest->input_token_len = 0;
559 #endif /* !CURL_DISABLE_CRYPTO_AUTH */
563 * Curl_sasl_create_ntlm_type1_message()
565 * This is used to generate an already encoded NTLM type-1 message ready for
566 * sending to the recipient.
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.
577 * Returns CURLE_OK on success.
579 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
581 struct ntlmdata *ntlm,
582 char **outptr, size_t *outlen)
584 PSecPkgInfo SecurityPackage;
585 SecBuffer type_1_buf;
586 SecBufferDesc type_1_desc;
587 SECURITY_STATUS status;
589 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
591 /* Clean up any former leftovers and initialise to defaults */
592 Curl_sasl_ntlm_cleanup(ntlm);
594 /* Query the security package for NTLM */
595 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
597 if(status != SEC_E_OK)
598 return CURLE_NOT_BUILT_IN;
600 ntlm->token_max = SecurityPackage->cbMaxToken;
602 /* Release the package buffer as it is not required anymore */
603 s_pSecFn->FreeContextBuffer(SecurityPackage);
605 /* Allocate our output buffer */
606 ntlm->output_token = malloc(ntlm->token_max);
607 if(!ntlm->output_token)
608 return CURLE_OUT_OF_MEMORY;
610 if(userp && *userp) {
613 /* Populate our identity structure */
614 result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
618 /* Allow proper cleanup of the identity structure */
619 ntlm->p_identity = &ntlm->identity;
622 /* Use the current Windows user */
623 ntlm->p_identity = NULL;
625 /* Allocate our credentials handle */
626 ntlm->credentials = malloc(sizeof(CredHandle));
627 if(!ntlm->credentials)
628 return CURLE_OUT_OF_MEMORY;
630 memset(ntlm->credentials, 0, sizeof(CredHandle));
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;
641 /* Allocate our new context handle */
642 ntlm->context = malloc(sizeof(CtxtHandle));
644 return CURLE_OUT_OF_MEMORY;
646 memset(ntlm->context, 0, sizeof(CtxtHandle));
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);
656 /* Generate our type-1 message */
657 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
659 0, 0, SECURITY_NETWORK_DREP,
661 ntlm->context, &type_1_desc,
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;
669 /* Base64 encode the response */
670 return Curl_base64_encode(NULL, (char *) ntlm->output_token,
671 type_1_buf.cbBuffer, outptr, outlen);
675 * Curl_sasl_decode_ntlm_type2_message()
677 * This is used to decode an already encoded NTLM type-2 message.
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.
685 * Returns CURLE_OK on success.
687 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
688 const char *type2msg,
689 struct ntlmdata *ntlm)
691 CURLcode result = CURLE_OK;
692 unsigned char *type2 = NULL;
693 size_t type2_len = 0;
695 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
699 /* Decode the base-64 encoded type-2 message */
700 if(strlen(type2msg) && *type2msg != '=') {
701 result = Curl_base64_decode(type2msg, &type2, &type2_len);
706 /* Ensure we have a valid type-2 message */
708 infof(data, "NTLM handshake failure (empty type-2 message)\n");
710 return CURLE_BAD_CONTENT_ENCODING;
713 /* Simply store the challenge for use later */
714 ntlm->input_token = type2;
715 ntlm->input_token_len = type2_len;
721 * Curl_sasl_create_ntlm_type3_message()
723 * This is used to generate an already encoded NTLM type-3 message ready for
724 * sending to the recipient.
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.
736 * Returns CURLE_OK on success.
738 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
741 struct ntlmdata *ntlm,
742 char **outptr, size_t *outlen)
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;
751 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
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);
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);
772 /* Generate our type-3 message */
773 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
776 0, 0, SECURITY_NETWORK_DREP,
781 if(status != SEC_E_OK) {
782 infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
785 return CURLE_RECV_ERROR;
788 /* Base64 encode the response */
789 result = Curl_base64_encode(data, (char *) ntlm->output_token,
790 type_3_buf.cbBuffer, outptr, outlen);
792 Curl_sasl_ntlm_cleanup(ntlm);
798 * Curl_sasl_ntlm_cleanup()
800 * This is used to clean up the ntlm specific data.
804 * ntlm [in/out] - The ntlm data struct being cleaned up.
807 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
809 /* Free our security context */
811 s_pSecFn->DeleteSecurityContext(ntlm->context);
813 ntlm->context = NULL;
816 /* Free our credentials handle */
817 if(ntlm->credentials) {
818 s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
819 free(ntlm->credentials);
820 ntlm->credentials = NULL;
823 /* Free our identity */
824 Curl_sspi_free_identity(ntlm->p_identity);
825 ntlm->p_identity = NULL;
827 /* Free the input and output tokens */
828 Curl_safefree(ntlm->input_token);
829 Curl_safefree(ntlm->output_token);
831 /* Reset any variables */
834 #endif /* USE_NTLM */
836 #if defined(USE_KERBEROS5)
838 * Curl_sasl_create_gssapi_user_message()
840 * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
841 * message ready for sending to the recipient.
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
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.
857 * Returns CURLE_OK on success.
859 CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
863 const bool mutual_auth,
865 struct kerberos5data *krb5,
866 char **outptr, size_t *outlen)
868 CURLcode result = CURLE_OK;
870 unsigned char *chlg = NULL;
872 PSecPkgInfo SecurityPackage;
875 SecBufferDesc chlg_desc;
876 SecBufferDesc resp_desc;
877 SECURITY_STATUS status;
879 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
881 if(!krb5->credentials) {
882 /* Query the security package for Kerberos */
883 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
884 TEXT(SP_NAME_KERBEROS),
886 if(status != SEC_E_OK) {
887 return CURLE_NOT_BUILT_IN;
890 krb5->token_max = SecurityPackage->cbMaxToken;
892 /* Release the package buffer as it is not required anymore */
893 s_pSecFn->FreeContextBuffer(SecurityPackage);
895 /* Allocate our response buffer */
896 krb5->output_token = malloc(krb5->token_max);
897 if(!krb5->output_token)
898 return CURLE_OUT_OF_MEMORY;
900 /* Generate our SPN */
901 krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
903 return CURLE_OUT_OF_MEMORY;
905 if(userp && *userp) {
906 /* Populate our identity structure */
907 result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
911 /* Allow proper cleanup of the identity structure */
912 krb5->p_identity = &krb5->identity;
915 /* Use the current Windows user */
916 krb5->p_identity = NULL;
918 /* Allocate our credentials handle */
919 krb5->credentials = malloc(sizeof(CredHandle));
920 if(!krb5->credentials)
921 return CURLE_OUT_OF_MEMORY;
923 memset(krb5->credentials, 0, sizeof(CredHandle));
925 /* Acquire our credentials handle */
926 status = s_pSecFn->AcquireCredentialsHandle(NULL,
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;
935 /* Allocate our new context handle */
936 krb5->context = malloc(sizeof(CtxtHandle));
938 return CURLE_OUT_OF_MEMORY;
940 memset(krb5->context, 0, sizeof(CtxtHandle));
943 /* Decode the base-64 encoded challenge message */
944 if(strlen(chlg64) && *chlg64 != '=') {
945 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
950 /* Ensure we have a valid challenge message */
952 infof(data, "GSSAPI handshake failure (empty challenge message)\n");
954 return CURLE_BAD_CONTENT_ENCODING;
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);
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);
974 /* Generate our challenge-response message */
975 status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
976 chlg ? krb5->context : NULL,
979 ISC_REQ_MUTUAL_AUTH : 0),
980 0, SECURITY_NATIVE_DREP,
981 chlg ? &chlg_desc : NULL, 0,
986 if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
989 return CURLE_RECV_ERROR;
992 if(memcmp(&context, krb5->context, sizeof(context))) {
993 s_pSecFn->DeleteSecurityContext(krb5->context);
995 memcpy(krb5->context, &context, sizeof(context));
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);
1004 /* Free the decoded challenge */
1011 * Curl_sasl_create_gssapi_security_message()
1013 * This is used to generate an already encoded GSSAPI (Kerberos V5) security
1014 * token message ready for sending to the recipient.
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.
1025 * Returns CURLE_OK on success.
1027 CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
1029 struct kerberos5data *krb5,
1033 CURLcode result = CURLE_OK;
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;
1057 /* Decode the base-64 encoded input message */
1058 if(strlen(chlg64) && *chlg64 != '=') {
1059 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
1064 /* Ensure we have a valid challenge message */
1066 infof(data, "GSSAPI handshake failure (empty security message)\n");
1068 return CURLE_BAD_CONTENT_ENCODING;
1071 /* Get our response size information */
1072 status = s_pSecFn->QueryContextAttributes(krb5->context,
1075 if(status != SEC_E_OK) {
1078 return CURLE_OUT_OF_MEMORY;
1081 /* Get the fully qualified username back from the context */
1082 status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
1083 SECPKG_CRED_ATTR_NAMES,
1085 if(status != SEC_E_OK) {
1088 return CURLE_RECV_ERROR;
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;
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");
1109 return CURLE_BAD_CONTENT_ENCODING;
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");
1118 return CURLE_BAD_CONTENT_ENCODING;
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);
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");
1131 return CURLE_BAD_CONTENT_ENCODING;
1134 /* Extract the maximum message size the server can receive */
1135 max_size = ntohl(indata & 0xFFFFFF00);
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. */
1143 /* Allocate the trailer */
1144 trailer = malloc(sizes.cbSecurityTrailer);
1146 return CURLE_OUT_OF_MEMORY;
1148 /* Convert the user name to UTF8 when operating with Unicode */
1149 user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
1153 return CURLE_OUT_OF_MEMORY;
1156 /* Allocate our message */
1157 messagelen = sizeof(outdata) + strlen(user_name) + 1;
1158 message = malloc(messagelen);
1161 Curl_unicodefree(user_name);
1163 return CURLE_OUT_OF_MEMORY;
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);
1176 /* Allocate the padding */
1177 padding = malloc(sizes.cbBlockSize);
1182 return CURLE_OUT_OF_MEMORY;
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;
1199 /* Encrypt the data */
1200 status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
1202 if(status != SEC_E_OK) {
1207 return CURLE_OUT_OF_MEMORY;
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);
1219 return CURLE_OUT_OF_MEMORY;
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);
1229 /* Base64 encode the response */
1230 result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
1233 /* Free all of our local buffers */
1243 * Curl_sasl_gssapi_cleanup()
1245 * This is used to clean up the gssapi specific data.
1249 * krb5 [in/out] - The kerberos 5 data struct being cleaned up.
1252 void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
1254 /* Free our security context */
1256 s_pSecFn->DeleteSecurityContext(krb5->context);
1257 free(krb5->context);
1258 krb5->context = NULL;
1261 /* Free our credentials handle */
1262 if(krb5->credentials) {
1263 s_pSecFn->FreeCredentialsHandle(krb5->credentials);
1264 free(krb5->credentials);
1265 krb5->credentials = NULL;
1268 /* Free our identity */
1269 Curl_sspi_free_identity(krb5->p_identity);
1270 krb5->p_identity = NULL;
1272 /* Free the SPN and output token */
1273 Curl_safefree(krb5->spn);
1274 Curl_safefree(krb5->output_token);
1276 /* Reset any variables */
1277 krb5->token_max = 0;
1279 #endif /* USE_KERBEROS5 */
1281 #endif /* USE_WINDOWS_SSPI */