1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2014 - 2016 Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) 2014 - 2016, 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 https://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/instance.
56 * serivce [in] - The service type such as www, smtp, pop or imap.
57 * instance [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 *instance)
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, instance);
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 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;
324 Curl_unicodefree(domain.tchar_ptr);
327 /* unknown specifier, ignore it! */
331 break; /* we're done here */
333 /* Pass all additional spaces here */
334 while(*chlg && ISSPACE(*chlg))
337 /* Allow the list to be comma-separated */
347 * Curl_sasl_decode_digest_http_message()
349 * This is used to decode a HTTP DIGEST challenge message into the seperate
354 * chlg [in] - The challenge message.
355 * digest [in/out] - The digest data struct being used and modified.
357 * Returns CURLE_OK on success.
359 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
360 struct digestdata *digest)
362 size_t chlglen = strlen(chlg);
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;
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;
374 digest->input_token_len = chlglen;
380 * Curl_sasl_create_digest_http_message()
382 * This is used to generate a HTTP DIGEST response message ready for sending
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.
397 * Returns CURLE_OK on success.
399 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
402 const unsigned char *request,
403 const unsigned char *uripath,
404 struct digestdata *digest,
405 char **outptr, size_t *outlen)
408 CredHandle credentials;
412 PSecPkgInfo SecurityPackage;
413 SEC_WINNT_AUTH_IDENTITY identity;
414 SEC_WINNT_AUTH_IDENTITY *p_identity;
415 SecBuffer chlg_buf[3];
417 SecBufferDesc chlg_desc;
418 SecBufferDesc resp_desc;
419 SECURITY_STATUS status;
421 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
426 /* Query the security package for DigestSSP */
427 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
429 if(status != SEC_E_OK)
430 return CURLE_NOT_BUILT_IN;
432 token_max = SecurityPackage->cbMaxToken;
434 /* Release the package buffer as it is not required anymore */
435 s_pSecFn->FreeContextBuffer(SecurityPackage);
437 /* Allocate the output buffer according to the max token size as indicated
438 by the security package */
439 output_token = malloc(token_max);
441 return CURLE_OUT_OF_MEMORY;
443 if(userp && *userp) {
444 /* Populate our identity structure */
445 if(Curl_create_sspi_identity(userp, passwdp, &identity))
446 return CURLE_OUT_OF_MEMORY;
448 /* Populate our identity domain */
449 if(Curl_override_sspi_http_realm((const char*)digest->input_token,
451 return CURLE_OUT_OF_MEMORY;
453 /* Allow proper cleanup of the identity structure */
454 p_identity = &identity;
457 /* Use the current Windows user */
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);
470 return CURLE_LOGIN_DENIED;
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;
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);
495 spn = Curl_convert_UTF8_to_tchar((char *) uripath);
497 Curl_sspi_free_identity(p_identity);
500 return CURLE_OUT_OF_MEMORY;
503 /* Generate our reponse message */
504 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
506 ISC_REQ_USE_HTTP_STYLE, 0, 0,
507 &chlg_desc, 0, &context,
508 &resp_desc, &attrs, &expiry);
509 Curl_unicodefree(spn);
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);
517 Curl_sspi_free_identity(p_identity);
520 return CURLE_OUT_OF_MEMORY;
523 resp = malloc(resp_buf.cbBuffer + 1);
525 s_pSecFn->DeleteSecurityContext(&context);
526 s_pSecFn->FreeCredentialsHandle(&credentials);
528 Curl_sspi_free_identity(p_identity);
531 return CURLE_OUT_OF_MEMORY;
534 /* Copy the generated reponse */
535 memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
536 resp[resp_buf.cbBuffer] = 0x00;
538 /* Return the response */
540 *outlen = resp_buf.cbBuffer;
542 /* Free our handles */
543 s_pSecFn->DeleteSecurityContext(&context);
544 s_pSecFn->FreeCredentialsHandle(&credentials);
546 /* Free the identity structure */
547 Curl_sspi_free_identity(p_identity);
549 /* Free the response buffer */
556 * Curl_sasl_digest_cleanup()
558 * This is used to clean up the digest specific data.
562 * digest [in/out] - The digest data struct being cleaned up.
565 void Curl_sasl_digest_cleanup(struct digestdata *digest)
567 /* Free the input token */
568 Curl_safefree(digest->input_token);
570 /* Reset any variables */
571 digest->input_token_len = 0;
573 #endif /* !CURL_DISABLE_CRYPTO_AUTH */
577 * Curl_sasl_create_ntlm_type1_message()
579 * This is used to generate an already encoded NTLM type-1 message ready for
580 * sending to the recipient.
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.
591 * Returns CURLE_OK on success.
593 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
595 struct ntlmdata *ntlm,
596 char **outptr, size_t *outlen)
598 PSecPkgInfo SecurityPackage;
599 SecBuffer type_1_buf;
600 SecBufferDesc type_1_desc;
601 SECURITY_STATUS status;
603 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
605 /* Clean up any former leftovers and initialise to defaults */
606 Curl_sasl_ntlm_cleanup(ntlm);
608 /* Query the security package for NTLM */
609 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
611 if(status != SEC_E_OK)
612 return CURLE_NOT_BUILT_IN;
614 ntlm->token_max = SecurityPackage->cbMaxToken;
616 /* Release the package buffer as it is not required anymore */
617 s_pSecFn->FreeContextBuffer(SecurityPackage);
619 /* Allocate our output buffer */
620 ntlm->output_token = malloc(ntlm->token_max);
621 if(!ntlm->output_token)
622 return CURLE_OUT_OF_MEMORY;
624 if(userp && *userp) {
627 /* Populate our identity structure */
628 result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
632 /* Allow proper cleanup of the identity structure */
633 ntlm->p_identity = &ntlm->identity;
636 /* Use the current Windows user */
637 ntlm->p_identity = NULL;
639 /* Allocate our credentials handle */
640 ntlm->credentials = malloc(sizeof(CredHandle));
641 if(!ntlm->credentials)
642 return CURLE_OUT_OF_MEMORY;
644 memset(ntlm->credentials, 0, sizeof(CredHandle));
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;
655 /* Allocate our new context handle */
656 ntlm->context = malloc(sizeof(CtxtHandle));
658 return CURLE_OUT_OF_MEMORY;
660 memset(ntlm->context, 0, sizeof(CtxtHandle));
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);
670 /* Generate our type-1 message */
671 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
673 0, 0, SECURITY_NETWORK_DREP,
675 ntlm->context, &type_1_desc,
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;
683 /* Base64 encode the response */
684 return Curl_base64_encode(NULL, (char *) ntlm->output_token,
685 type_1_buf.cbBuffer, outptr, outlen);
689 * Curl_sasl_decode_ntlm_type2_message()
691 * This is used to decode an already encoded NTLM type-2 message.
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.
699 * Returns CURLE_OK on success.
701 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
702 const char *type2msg,
703 struct ntlmdata *ntlm)
705 CURLcode result = CURLE_OK;
706 unsigned char *type2 = NULL;
707 size_t type2_len = 0;
709 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
713 /* Decode the base-64 encoded type-2 message */
714 if(strlen(type2msg) && *type2msg != '=') {
715 result = Curl_base64_decode(type2msg, &type2, &type2_len);
720 /* Ensure we have a valid type-2 message */
722 infof(data, "NTLM handshake failure (empty type-2 message)\n");
724 return CURLE_BAD_CONTENT_ENCODING;
727 /* Simply store the challenge for use later */
728 ntlm->input_token = type2;
729 ntlm->input_token_len = type2_len;
735 * Curl_sasl_create_ntlm_type3_message()
737 * This is used to generate an already encoded NTLM type-3 message ready for
738 * sending to the recipient.
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.
750 * Returns CURLE_OK on success.
752 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
755 struct ntlmdata *ntlm,
756 char **outptr, size_t *outlen)
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;
765 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
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);
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);
786 /* Generate our type-3 message */
787 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
790 0, 0, SECURITY_NETWORK_DREP,
795 if(status != SEC_E_OK) {
796 infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
799 return CURLE_RECV_ERROR;
802 /* Base64 encode the response */
803 result = Curl_base64_encode(data, (char *) ntlm->output_token,
804 type_3_buf.cbBuffer, outptr, outlen);
806 Curl_sasl_ntlm_cleanup(ntlm);
812 * Curl_sasl_ntlm_cleanup()
814 * This is used to clean up the NTLM specific data.
818 * ntlm [in/out] - The NTLM data struct being cleaned up.
821 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
823 /* Free our security context */
825 s_pSecFn->DeleteSecurityContext(ntlm->context);
827 ntlm->context = NULL;
830 /* Free our credentials handle */
831 if(ntlm->credentials) {
832 s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
833 free(ntlm->credentials);
834 ntlm->credentials = NULL;
837 /* Free our identity */
838 Curl_sspi_free_identity(ntlm->p_identity);
839 ntlm->p_identity = NULL;
841 /* Free the input and output tokens */
842 Curl_safefree(ntlm->input_token);
843 Curl_safefree(ntlm->output_token);
845 /* Reset any variables */
848 #endif /* USE_NTLM */
850 #if defined(USE_KERBEROS5)
852 * Curl_sasl_create_gssapi_user_message()
854 * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
855 * message ready for sending to the recipient.
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
865 * chlg64 [in] - The optional base64 encoded challenge message.
866 * krb5 [in/out] - The Kerberos 5 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.
871 * Returns CURLE_OK on success.
873 CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
877 const bool mutual_auth,
879 struct kerberos5data *krb5,
880 char **outptr, size_t *outlen)
882 CURLcode result = CURLE_OK;
884 unsigned char *chlg = NULL;
886 PSecPkgInfo SecurityPackage;
889 SecBufferDesc chlg_desc;
890 SecBufferDesc resp_desc;
891 SECURITY_STATUS status;
893 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
895 if(!krb5->credentials) {
896 /* Query the security package for Kerberos */
897 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
898 TEXT(SP_NAME_KERBEROS),
900 if(status != SEC_E_OK) {
901 return CURLE_NOT_BUILT_IN;
904 krb5->token_max = SecurityPackage->cbMaxToken;
906 /* Release the package buffer as it is not required anymore */
907 s_pSecFn->FreeContextBuffer(SecurityPackage);
909 /* Allocate our response buffer */
910 krb5->output_token = malloc(krb5->token_max);
911 if(!krb5->output_token)
912 return CURLE_OUT_OF_MEMORY;
914 /* Generate our SPN */
915 krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
917 return CURLE_OUT_OF_MEMORY;
919 if(userp && *userp) {
920 /* Populate our identity structure */
921 result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
925 /* Allow proper cleanup of the identity structure */
926 krb5->p_identity = &krb5->identity;
929 /* Use the current Windows user */
930 krb5->p_identity = NULL;
932 /* Allocate our credentials handle */
933 krb5->credentials = malloc(sizeof(CredHandle));
934 if(!krb5->credentials)
935 return CURLE_OUT_OF_MEMORY;
937 memset(krb5->credentials, 0, sizeof(CredHandle));
939 /* Acquire our credentials handle */
940 status = s_pSecFn->AcquireCredentialsHandle(NULL,
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;
949 /* Allocate our new context handle */
950 krb5->context = malloc(sizeof(CtxtHandle));
952 return CURLE_OUT_OF_MEMORY;
954 memset(krb5->context, 0, sizeof(CtxtHandle));
957 /* Decode the base-64 encoded challenge message */
958 if(strlen(chlg64) && *chlg64 != '=') {
959 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
964 /* Ensure we have a valid challenge message */
966 infof(data, "GSSAPI handshake failure (empty challenge message)\n");
968 return CURLE_BAD_CONTENT_ENCODING;
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);
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);
988 /* Generate our challenge-response message */
989 status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
990 chlg ? krb5->context : NULL,
993 ISC_REQ_MUTUAL_AUTH : 0),
994 0, SECURITY_NATIVE_DREP,
995 chlg ? &chlg_desc : NULL, 0,
1000 if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
1003 return CURLE_RECV_ERROR;
1006 if(memcmp(&context, krb5->context, sizeof(context))) {
1007 s_pSecFn->DeleteSecurityContext(krb5->context);
1009 memcpy(krb5->context, &context, sizeof(context));
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);
1018 /* Free the decoded challenge */
1025 * Curl_sasl_create_gssapi_security_message()
1027 * This is used to generate an already encoded GSSAPI (Kerberos V5) security
1028 * token message ready for sending to the recipient.
1032 * data [in] - The session handle.
1033 * chlg64 [in] - The optional base64 encoded challenge message.
1034 * krb5 [in/out] - The Kerberos 5 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.
1039 * Returns CURLE_OK on success.
1041 CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
1043 struct kerberos5data *krb5,
1047 CURLcode result = CURLE_OK;
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;
1071 /* Decode the base-64 encoded input message */
1072 if(strlen(chlg64) && *chlg64 != '=') {
1073 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
1078 /* Ensure we have a valid challenge message */
1080 infof(data, "GSSAPI handshake failure (empty security message)\n");
1082 return CURLE_BAD_CONTENT_ENCODING;
1085 /* Get our response size information */
1086 status = s_pSecFn->QueryContextAttributes(krb5->context,
1089 if(status != SEC_E_OK) {
1092 return CURLE_OUT_OF_MEMORY;
1095 /* Get the fully qualified username back from the context */
1096 status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
1097 SECPKG_CRED_ATTR_NAMES,
1099 if(status != SEC_E_OK) {
1102 return CURLE_RECV_ERROR;
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;
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");
1123 return CURLE_BAD_CONTENT_ENCODING;
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");
1132 return CURLE_BAD_CONTENT_ENCODING;
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);
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");
1145 return CURLE_BAD_CONTENT_ENCODING;
1148 /* Extract the maximum message size the server can receive */
1149 max_size = ntohl(indata & 0xFFFFFF00);
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. */
1157 /* Allocate the trailer */
1158 trailer = malloc(sizes.cbSecurityTrailer);
1160 return CURLE_OUT_OF_MEMORY;
1162 /* Convert the user name to UTF8 when operating with Unicode */
1163 user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
1167 return CURLE_OUT_OF_MEMORY;
1170 /* Allocate our message */
1171 messagelen = sizeof(outdata) + strlen(user_name) + 1;
1172 message = malloc(messagelen);
1175 Curl_unicodefree(user_name);
1177 return CURLE_OUT_OF_MEMORY;
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);
1190 /* Allocate the padding */
1191 padding = malloc(sizes.cbBlockSize);
1196 return CURLE_OUT_OF_MEMORY;
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;
1213 /* Encrypt the data */
1214 status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
1216 if(status != SEC_E_OK) {
1221 return CURLE_OUT_OF_MEMORY;
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);
1233 return CURLE_OUT_OF_MEMORY;
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);
1243 /* Base64 encode the response */
1244 result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
1247 /* Free all of our local buffers */
1257 * Curl_sasl_gssapi_cleanup()
1259 * This is used to clean up the GSSAPI (Kerberos V5) specific data.
1263 * krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
1266 void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
1268 /* Free our security context */
1270 s_pSecFn->DeleteSecurityContext(krb5->context);
1271 free(krb5->context);
1272 krb5->context = NULL;
1275 /* Free our credentials handle */
1276 if(krb5->credentials) {
1277 s_pSecFn->FreeCredentialsHandle(krb5->credentials);
1278 free(krb5->credentials);
1279 krb5->credentials = NULL;
1282 /* Free our identity */
1283 Curl_sspi_free_identity(krb5->p_identity);
1284 krb5->p_identity = NULL;
1286 /* Free the SPN and output token */
1287 Curl_safefree(krb5->spn);
1288 Curl_safefree(krb5->output_token);
1290 /* Reset any variables */
1291 krb5->token_max = 0;
1293 #endif /* USE_KERBEROS5 */
1295 #endif /* USE_WINDOWS_SSPI */