1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2014, Steve Holme, <steve_holme@hotmail.com>.
9 * Copyright (C) 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
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_memory.h"
40 #include "curl_multibyte.h"
44 #define _MPRINTF_REPLACE /* use our functions only */
45 #include <curl/mprintf.h>
47 /* The last #include file should be: */
51 * Curl_sasl_build_spn()
53 * This is used to build a SPN string in the format service/host.
57 * serivce [in] - The service type such as www, smtp, pop or imap.
58 * host [in] - The host name or realm.
60 * Returns a pointer to the newly allocated SPN.
62 TCHAR *Curl_sasl_build_spn(const char *service, const char *host)
64 char *utf8_spn = NULL;
65 TCHAR *tchar_spn = NULL;
67 /* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather
68 than doing this ourselves but the first is only available in Windows XP
69 and Windows Server 2003 and the latter is only available in Windows 2000
70 but not Windows95/98/ME or Windows NT4.0 unless the Active Directory
71 Client Extensions are installed. As such it is far simpler for us to
72 formulate the SPN instead. */
74 /* Allocate our UTF8 based SPN */
75 utf8_spn = aprintf("%s/%s", service, host);
80 /* Allocate our TCHAR based SPN */
81 tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn);
83 Curl_safefree(utf8_spn);
88 /* Release the UTF8 variant when operating with Unicode */
89 Curl_unicodefree(utf8_spn);
91 /* Return our newly allocated SPN */
95 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
97 * Curl_sasl_create_digest_md5_message()
99 * This is used to generate an already encoded DIGEST-MD5 response message
100 * ready for sending to the recipient.
104 * data [in] - The session handle.
105 * chlg64 [in] - The base64 encoded challenge message.
106 * userp [in] - The user name in the format User or Domain\User.
107 * passdwp [in] - The user's password.
108 * service [in] - The service type such as www, smtp, pop or imap.
109 * outptr [in/out] - The address where a pointer to newly allocated memory
110 * holding the result will be stored upon completion.
111 * outlen [out] - The length of the output message.
113 * Returns CURLE_OK on success.
115 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
120 char **outptr, size_t *outlen)
122 CURLcode result = CURLE_OK;
125 size_t token_max = 0;
126 unsigned char *input_token = NULL;
127 unsigned char *output_token = NULL;
128 CredHandle credentials;
130 PSecPkgInfo SecurityPackage;
131 SEC_WINNT_AUTH_IDENTITY identity;
132 SEC_WINNT_AUTH_IDENTITY *p_identity;
135 SecBufferDesc chlg_desc;
136 SecBufferDesc resp_desc;
137 SECURITY_STATUS status;
139 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
141 /* Decode the base-64 encoded challenge message */
142 if(strlen(chlg64) && *chlg64 != '=') {
143 result = Curl_base64_decode(chlg64, &input_token, &chlglen);
148 /* Ensure we have a valid challenge message */
150 infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
152 return CURLE_BAD_CONTENT_ENCODING;
155 /* Query the security package for DigestSSP */
156 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
158 if(status != SEC_E_OK) {
159 Curl_safefree(input_token);
161 return CURLE_NOT_BUILT_IN;
164 token_max = SecurityPackage->cbMaxToken;
166 /* Release the package buffer as it is not required anymore */
167 s_pSecFn->FreeContextBuffer(SecurityPackage);
169 /* Allocate our response buffer */
170 output_token = malloc(token_max);
172 Curl_safefree(input_token);
174 return CURLE_OUT_OF_MEMORY;
177 /* Generate our SPN */
178 spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
180 Curl_safefree(output_token);
181 Curl_safefree(input_token);
183 return CURLE_OUT_OF_MEMORY;
186 if(userp && *userp) {
187 /* Populate our identity structure */
188 result = Curl_create_sspi_identity(userp, passwdp, &identity);
191 Curl_safefree(output_token);
192 Curl_safefree(input_token);
197 /* Allow proper cleanup of the identity structure */
198 p_identity = &identity;
201 /* Use the current Windows user */
204 /* Acquire our credentials handle */
205 status = s_pSecFn->AcquireCredentialsHandle(NULL,
206 (TCHAR *) TEXT(SP_NAME_DIGEST),
207 SECPKG_CRED_OUTBOUND, NULL,
208 p_identity, NULL, NULL,
209 &credentials, &expiry);
211 if(status != SEC_E_OK) {
212 Curl_sspi_free_identity(p_identity);
214 Curl_safefree(output_token);
215 Curl_safefree(input_token);
217 return CURLE_LOGIN_DENIED;
220 /* Setup the challenge "input" security buffer */
221 chlg_desc.ulVersion = SECBUFFER_VERSION;
222 chlg_desc.cBuffers = 1;
223 chlg_desc.pBuffers = &chlg_buf;
224 chlg_buf.BufferType = SECBUFFER_TOKEN;
225 chlg_buf.pvBuffer = input_token;
226 chlg_buf.cbBuffer = curlx_uztoul(chlglen);
228 /* Setup the response "output" security buffer */
229 resp_desc.ulVersion = SECBUFFER_VERSION;
230 resp_desc.cBuffers = 1;
231 resp_desc.pBuffers = &resp_buf;
232 resp_buf.BufferType = SECBUFFER_TOKEN;
233 resp_buf.pvBuffer = output_token;
234 resp_buf.cbBuffer = curlx_uztoul(token_max);
236 /* Generate our response message */
237 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
238 0, 0, 0, &chlg_desc, 0,
239 &context, &resp_desc, &attrs,
242 if(status == SEC_I_COMPLETE_NEEDED ||
243 status == SEC_I_COMPLETE_AND_CONTINUE)
244 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
245 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
246 s_pSecFn->FreeCredentialsHandle(&credentials);
247 Curl_sspi_free_identity(p_identity);
249 Curl_safefree(output_token);
250 Curl_safefree(input_token);
252 return CURLE_RECV_ERROR;
255 /* Base64 encode the response */
256 result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
259 /* Free our handles */
260 s_pSecFn->DeleteSecurityContext(&context);
261 s_pSecFn->FreeCredentialsHandle(&credentials);
263 /* Free the identity structure */
264 Curl_sspi_free_identity(p_identity);
269 /* Free the response buffer */
270 Curl_safefree(output_token);
272 /* Free the decoded challenge message */
273 Curl_safefree(input_token);
279 * Curl_sasl_decode_digest_http_message()
281 * This is used to decode a HTTP DIGEST challenge message into the seperate
286 * chlg [in] - The challenge message.
287 * digest [in/out] - The digest data struct being used and modified.
289 * Returns CURLE_OK on success.
291 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
292 struct digestdata *digest)
294 size_t chlglen = strlen(chlg);
296 /* We had an input token before and we got another one now. This means we
297 provided bad credentials in the previous request. */
298 if(digest->input_token)
299 return CURLE_BAD_CONTENT_ENCODING;
301 /* Simply store the challenge for use later */
302 digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
303 if(!digest->input_token)
304 return CURLE_OUT_OF_MEMORY;
306 digest->input_token_len = chlglen;
312 * Curl_sasl_create_digest_http_message()
314 * This is used to generate a HTTP DIGEST response message ready for sending
319 * data [in] - The session handle.
320 * userp [in] - The user name in the format User or Domain\User.
321 * passdwp [in] - The user's password.
322 * request [in] - The HTTP request.
323 * uripath [in] - The path of the HTTP uri.
324 * digest [in/out] - The digest data struct being used and modified.
325 * outptr [in/out] - The address where a pointer to newly allocated memory
326 * holding the result will be stored upon completion.
327 * outlen [out] - The length of the output message.
329 * Returns CURLE_OK on success.
331 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
334 const unsigned char *request,
335 const unsigned char *uripath,
336 struct digestdata *digest,
337 char **outptr, size_t *outlen)
340 CredHandle credentials;
344 PSecPkgInfo SecurityPackage;
345 SEC_WINNT_AUTH_IDENTITY identity;
346 SEC_WINNT_AUTH_IDENTITY *p_identity;
347 SecBuffer chlg_buf[3];
349 SecBufferDesc chlg_desc;
350 SecBufferDesc resp_desc;
351 SECURITY_STATUS status;
353 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
357 /* Query the security package for DigestSSP */
358 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
360 if(status != SEC_E_OK)
361 return CURLE_NOT_BUILT_IN;
363 token_max = SecurityPackage->cbMaxToken;
365 /* Release the package buffer as it is not required anymore */
366 s_pSecFn->FreeContextBuffer(SecurityPackage);
368 /* Allocate the output buffer according to the max token size as indicated
369 by the security package */
370 output_token = malloc(token_max);
372 return CURLE_OUT_OF_MEMORY;
374 if(userp && *userp) {
375 /* Populate our identity structure */
376 if(Curl_create_sspi_identity(userp, passwdp, &identity))
377 return CURLE_OUT_OF_MEMORY;
379 /* Allow proper cleanup of the identity structure */
380 p_identity = &identity;
383 /* Use the current Windows user */
386 /* Acquire our credentials handle */
387 status = s_pSecFn->AcquireCredentialsHandle(NULL,
388 (TCHAR *) TEXT(SP_NAME_DIGEST),
389 SECPKG_CRED_OUTBOUND, NULL,
390 p_identity, NULL, NULL,
391 &credentials, &expiry);
392 if(status != SEC_E_OK) {
393 Curl_safefree(output_token);
395 return CURLE_LOGIN_DENIED;
398 /* Setup the challenge "input" security buffer if present */
399 chlg_desc.ulVersion = SECBUFFER_VERSION;
400 chlg_desc.cBuffers = 3;
401 chlg_desc.pBuffers = chlg_buf;
402 chlg_buf[0].BufferType = SECBUFFER_TOKEN;
403 chlg_buf[0].pvBuffer = digest->input_token;
404 chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
405 chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
406 chlg_buf[1].pvBuffer = (void *)request;
407 chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
408 chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
409 chlg_buf[2].pvBuffer = NULL;
410 chlg_buf[2].cbBuffer = 0;
412 /* Setup the response "output" security buffer */
413 resp_desc.ulVersion = SECBUFFER_VERSION;
414 resp_desc.cBuffers = 1;
415 resp_desc.pBuffers = &resp_buf;
416 resp_buf.BufferType = SECBUFFER_TOKEN;
417 resp_buf.pvBuffer = output_token;
418 resp_buf.cbBuffer = curlx_uztoul(token_max);
420 /* Generate our reponse message */
421 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
423 ISC_REQ_USE_HTTP_STYLE, 0, 0,
424 &chlg_desc, 0, &context,
425 &resp_desc, &attrs, &expiry);
427 if(status == SEC_I_COMPLETE_NEEDED ||
428 status == SEC_I_COMPLETE_AND_CONTINUE)
429 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
430 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
431 s_pSecFn->FreeCredentialsHandle(&credentials);
433 Curl_safefree(output_token);
435 return CURLE_OUT_OF_MEMORY;
438 resp = malloc(resp_buf.cbBuffer + 1);
440 s_pSecFn->DeleteSecurityContext(&context);
441 s_pSecFn->FreeCredentialsHandle(&credentials);
443 Curl_safefree(output_token);
445 return CURLE_OUT_OF_MEMORY;
448 /* Copy the generated reponse */
449 memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
450 resp[resp_buf.cbBuffer] = 0x00;
452 /* Return the response */
454 *outlen = resp_buf.cbBuffer;
456 /* Free our handles */
457 s_pSecFn->DeleteSecurityContext(&context);
458 s_pSecFn->FreeCredentialsHandle(&credentials);
460 /* Free the identity structure */
461 Curl_sspi_free_identity(p_identity);
463 /* Free the response buffer */
464 Curl_safefree(output_token);
470 * Curl_sasl_digest_cleanup()
472 * This is used to clean up the digest specific data.
476 * digest [in/out] - The digest data struct being cleaned up.
479 void Curl_sasl_digest_cleanup(struct digestdata *digest)
481 /* Free the input token */
482 Curl_safefree(digest->input_token);
484 /* Reset any variables */
485 digest->input_token_len = 0;
487 #endif /* !CURL_DISABLE_CRYPTO_AUTH */
491 * Curl_sasl_create_ntlm_type1_message()
493 * This is used to generate an already encoded NTLM type-1 message ready for
494 * sending to the recipient.
498 * userp [in] - The user name in the format User or Domain\User.
499 * passdwp [in] - The user's password.
500 * ntlm [in/out] - The ntlm data struct being used and modified.
501 * outptr [in/out] - The address where a pointer to newly allocated memory
502 * holding the result will be stored upon completion.
503 * outlen [out] - The length of the output message.
505 * Returns CURLE_OK on success.
507 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
509 struct ntlmdata *ntlm,
510 char **outptr, size_t *outlen)
512 PSecPkgInfo SecurityPackage;
513 SecBuffer type_1_buf;
514 SecBufferDesc type_1_desc;
515 SECURITY_STATUS status;
517 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
519 /* Clean up any former leftovers and initialise to defaults */
520 Curl_sasl_ntlm_cleanup(ntlm);
522 /* Query the security package for NTLM */
523 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
525 if(status != SEC_E_OK)
526 return CURLE_NOT_BUILT_IN;
528 ntlm->token_max = SecurityPackage->cbMaxToken;
530 /* Release the package buffer as it is not required anymore */
531 s_pSecFn->FreeContextBuffer(SecurityPackage);
533 /* Allocate our output buffer */
534 ntlm->output_token = malloc(ntlm->token_max);
535 if(!ntlm->output_token)
536 return CURLE_OUT_OF_MEMORY;
538 if(userp && *userp) {
541 /* Populate our identity structure */
542 result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
546 /* Allow proper cleanup of the identity structure */
547 ntlm->p_identity = &ntlm->identity;
550 /* Use the current Windows user */
551 ntlm->p_identity = NULL;
553 /* Allocate our credentials handle */
554 ntlm->credentials = malloc(sizeof(CredHandle));
555 if(!ntlm->credentials)
556 return CURLE_OUT_OF_MEMORY;
558 memset(ntlm->credentials, 0, sizeof(CredHandle));
560 /* Acquire our credentials handle */
561 status = s_pSecFn->AcquireCredentialsHandle(NULL,
562 (TCHAR *) TEXT(SP_NAME_NTLM),
563 SECPKG_CRED_OUTBOUND, NULL,
564 ntlm->p_identity, NULL, NULL,
565 ntlm->credentials, &expiry);
566 if(status != SEC_E_OK)
567 return CURLE_LOGIN_DENIED;
569 /* Allocate our new context handle */
570 ntlm->context = malloc(sizeof(CtxtHandle));
572 return CURLE_OUT_OF_MEMORY;
574 memset(ntlm->context, 0, sizeof(CtxtHandle));
576 /* Setup the type-1 "output" security buffer */
577 type_1_desc.ulVersion = SECBUFFER_VERSION;
578 type_1_desc.cBuffers = 1;
579 type_1_desc.pBuffers = &type_1_buf;
580 type_1_buf.BufferType = SECBUFFER_TOKEN;
581 type_1_buf.pvBuffer = ntlm->output_token;
582 type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
584 /* Generate our type-1 message */
585 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
587 0, 0, SECURITY_NETWORK_DREP,
589 ntlm->context, &type_1_desc,
591 if(status == SEC_I_COMPLETE_NEEDED ||
592 status == SEC_I_COMPLETE_AND_CONTINUE)
593 s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
594 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
595 return CURLE_RECV_ERROR;
597 /* Base64 encode the response */
598 return Curl_base64_encode(NULL, (char *) ntlm->output_token,
599 type_1_buf.cbBuffer, outptr, outlen);
603 * Curl_sasl_decode_ntlm_type2_message()
605 * This is used to decode an already encoded NTLM type-2 message.
609 * data [in] - The session handle.
610 * type2msg [in] - The base64 encoded type-2 message.
611 * ntlm [in/out] - The ntlm data struct being used and modified.
613 * Returns CURLE_OK on success.
615 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
616 const char *type2msg,
617 struct ntlmdata *ntlm)
619 CURLcode result = CURLE_OK;
620 unsigned char *type2 = NULL;
621 size_t type2_len = 0;
623 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
627 /* Decode the base-64 encoded type-2 message */
628 if(strlen(type2msg) && *type2msg != '=') {
629 result = Curl_base64_decode(type2msg, &type2, &type2_len);
634 /* Ensure we have a valid type-2 message */
636 infof(data, "NTLM handshake failure (empty type-2 message)\n");
638 return CURLE_BAD_CONTENT_ENCODING;
641 /* Simply store the challenge for use later */
642 ntlm->input_token = type2;
643 ntlm->input_token_len = type2_len;
649 * Curl_sasl_create_ntlm_type3_message()
651 * This is used to generate an already encoded NTLM type-3 message ready for
652 * sending to the recipient.
656 * data [in] - The session handle.
657 * userp [in] - The user name in the format User or Domain\User.
658 * passdwp [in] - The user's password.
659 * ntlm [in/out] - The ntlm data struct being used and modified.
660 * outptr [in/out] - The address where a pointer to newly allocated memory
661 * holding the result will be stored upon completion.
662 * outlen [out] - The length of the output message.
664 * Returns CURLE_OK on success.
666 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
669 struct ntlmdata *ntlm,
670 char **outptr, size_t *outlen)
672 CURLcode result = CURLE_OK;
673 SecBuffer type_2_buf;
674 SecBuffer type_3_buf;
675 SecBufferDesc type_2_desc;
676 SecBufferDesc type_3_desc;
677 SECURITY_STATUS status;
679 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
684 /* Setup the type-2 "input" security buffer */
685 type_2_desc.ulVersion = SECBUFFER_VERSION;
686 type_2_desc.cBuffers = 1;
687 type_2_desc.pBuffers = &type_2_buf;
688 type_2_buf.BufferType = SECBUFFER_TOKEN;
689 type_2_buf.pvBuffer = ntlm->input_token;
690 type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len);
692 /* Setup the type-3 "output" security buffer */
693 type_3_desc.ulVersion = SECBUFFER_VERSION;
694 type_3_desc.cBuffers = 1;
695 type_3_desc.pBuffers = &type_3_buf;
696 type_3_buf.BufferType = SECBUFFER_TOKEN;
697 type_3_buf.pvBuffer = ntlm->output_token;
698 type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
700 /* Generate our type-3 message */
701 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
704 0, 0, SECURITY_NETWORK_DREP,
709 if(status != SEC_E_OK) {
710 infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
713 return CURLE_RECV_ERROR;
716 /* Base64 encode the response */
717 result = Curl_base64_encode(data, (char *) ntlm->output_token,
718 type_3_buf.cbBuffer, outptr, outlen);
720 Curl_sasl_ntlm_cleanup(ntlm);
726 * Curl_sasl_ntlm_cleanup()
728 * This is used to clean up the ntlm specific data.
732 * ntlm [in/out] - The ntlm data struct being cleaned up.
735 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
737 /* Free our security context */
739 s_pSecFn->DeleteSecurityContext(ntlm->context);
741 ntlm->context = NULL;
744 /* Free our credentials handle */
745 if(ntlm->credentials) {
746 s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
747 free(ntlm->credentials);
748 ntlm->credentials = NULL;
751 /* Free our identity */
752 Curl_sspi_free_identity(ntlm->p_identity);
753 ntlm->p_identity = NULL;
755 /* Free the input and output tokens */
756 Curl_safefree(ntlm->input_token);
757 Curl_safefree(ntlm->output_token);
759 /* Reset any variables */
762 #endif /* USE_NTLM */
764 #if defined(USE_KERBEROS5)
766 * Curl_sasl_create_gssapi_user_message()
768 * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
769 * message ready for sending to the recipient.
773 * data [in] - The session handle.
774 * userp [in] - The user name in the format User or Domain\User.
775 * passdwp [in] - The user's password.
776 * service [in] - The service type such as www, smtp, pop or imap.
777 * mutual_auth [in] - Flag specifing whether or not mutual authentication
779 * chlg64 [in] - The optional base64 encoded challenge message.
780 * krb5 [in/out] - The gssapi data struct being used and modified.
781 * outptr [in/out] - The address where a pointer to newly allocated memory
782 * holding the result will be stored upon completion.
783 * outlen [out] - The length of the output message.
785 * Returns CURLE_OK on success.
787 CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
791 const bool mutual_auth,
793 struct kerberos5data *krb5,
794 char **outptr, size_t *outlen)
796 CURLcode result = CURLE_OK;
798 unsigned char *chlg = NULL;
800 PSecPkgInfo SecurityPackage;
803 SecBufferDesc chlg_desc;
804 SecBufferDesc resp_desc;
805 SECURITY_STATUS status;
807 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
809 if(!krb5->credentials) {
810 /* Query the security package for Kerberos */
811 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
812 TEXT(SP_NAME_KERBEROS),
814 if(status != SEC_E_OK) {
815 return CURLE_NOT_BUILT_IN;
818 krb5->token_max = SecurityPackage->cbMaxToken;
820 /* Release the package buffer as it is not required anymore */
821 s_pSecFn->FreeContextBuffer(SecurityPackage);
823 /* Allocate our response buffer */
824 krb5->output_token = malloc(krb5->token_max);
825 if(!krb5->output_token)
826 return CURLE_OUT_OF_MEMORY;
828 /* Generate our SPN */
829 krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
831 return CURLE_OUT_OF_MEMORY;
833 if(userp && *userp) {
834 /* Populate our identity structure */
835 result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
839 /* Allow proper cleanup of the identity structure */
840 krb5->p_identity = &krb5->identity;
843 /* Use the current Windows user */
844 krb5->p_identity = NULL;
846 /* Allocate our credentials handle */
847 krb5->credentials = malloc(sizeof(CredHandle));
848 if(!krb5->credentials)
849 return CURLE_OUT_OF_MEMORY;
851 memset(krb5->credentials, 0, sizeof(CredHandle));
853 /* Acquire our credentials handle */
854 status = s_pSecFn->AcquireCredentialsHandle(NULL,
856 TEXT(SP_NAME_KERBEROS),
857 SECPKG_CRED_OUTBOUND, NULL,
858 krb5->p_identity, NULL, NULL,
859 krb5->credentials, &expiry);
860 if(status != SEC_E_OK)
861 return CURLE_LOGIN_DENIED;
863 /* Allocate our new context handle */
864 krb5->context = malloc(sizeof(CtxtHandle));
866 return CURLE_OUT_OF_MEMORY;
868 memset(krb5->context, 0, sizeof(CtxtHandle));
871 /* Decode the base-64 encoded challenge message */
872 if(strlen(chlg64) && *chlg64 != '=') {
873 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
878 /* Ensure we have a valid challenge message */
880 infof(data, "GSSAPI handshake failure (empty challenge message)\n");
882 return CURLE_BAD_CONTENT_ENCODING;
885 /* Setup the challenge "input" security buffer */
886 chlg_desc.ulVersion = SECBUFFER_VERSION;
887 chlg_desc.cBuffers = 1;
888 chlg_desc.pBuffers = &chlg_buf;
889 chlg_buf.BufferType = SECBUFFER_TOKEN;
890 chlg_buf.pvBuffer = chlg;
891 chlg_buf.cbBuffer = curlx_uztoul(chlglen);
894 /* Setup the response "output" security buffer */
895 resp_desc.ulVersion = SECBUFFER_VERSION;
896 resp_desc.cBuffers = 1;
897 resp_desc.pBuffers = &resp_buf;
898 resp_buf.BufferType = SECBUFFER_TOKEN;
899 resp_buf.pvBuffer = krb5->output_token;
900 resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
902 /* Generate our challenge-response message */
903 status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
904 chlg ? krb5->context : NULL,
907 ISC_REQ_MUTUAL_AUTH : 0),
908 0, SECURITY_NATIVE_DREP,
909 chlg ? &chlg_desc : NULL, 0,
914 if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
917 return CURLE_RECV_ERROR;
920 if(memcmp(&context, krb5->context, sizeof(context))) {
921 s_pSecFn->DeleteSecurityContext(krb5->context);
923 memcpy(krb5->context, &context, sizeof(context));
926 if(resp_buf.cbBuffer) {
927 /* Base64 encode the response */
928 result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer,
929 resp_buf.cbBuffer, outptr, outlen);
932 /* Free the decoded challenge */
939 * Curl_sasl_create_gssapi_security_message()
941 * This is used to generate an already encoded GSSAPI (Kerberos V5) security
942 * token message ready for sending to the recipient.
946 * data [in] - The session handle.
947 * chlg64 [in] - The optional base64 encoded challenge message.
948 * krb5 [in/out] - The gssapi data struct being used and modified.
949 * outptr [in/out] - The address where a pointer to newly allocated memory
950 * holding the result will be stored upon completion.
951 * outlen [out] - The length of the output message.
953 * Returns CURLE_OK on success.
955 CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
957 struct kerberos5data *krb5,
961 CURLcode result = CURLE_OK;
964 size_t messagelen = 0;
965 size_t appdatalen = 0;
966 unsigned char *chlg = NULL;
967 unsigned char *trailer = NULL;
968 unsigned char *message = NULL;
969 unsigned char *padding = NULL;
970 unsigned char *appdata = NULL;
971 SecBuffer input_buf[2];
972 SecBuffer wrap_buf[3];
973 SecBufferDesc input_desc;
974 SecBufferDesc wrap_desc;
975 unsigned long indata = 0;
976 unsigned long outdata = 0;
977 unsigned long qop = 0;
978 unsigned long sec_layer = 0;
979 unsigned long max_size = 0;
980 SecPkgContext_Sizes sizes;
981 SecPkgCredentials_Names names;
982 SECURITY_STATUS status;
985 /* Decode the base-64 encoded input message */
986 if(strlen(chlg64) && *chlg64 != '=') {
987 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
992 /* Ensure we have a valid challenge message */
994 infof(data, "GSSAPI handshake failure (empty security message)\n");
996 return CURLE_BAD_CONTENT_ENCODING;
999 /* Get our response size information */
1000 status = s_pSecFn->QueryContextAttributes(krb5->context,
1003 if(status != SEC_E_OK) {
1004 Curl_safefree(chlg);
1006 return CURLE_OUT_OF_MEMORY;
1009 /* Get the fully qualified username back from the context */
1010 status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
1011 SECPKG_CRED_ATTR_NAMES,
1013 if(status != SEC_E_OK) {
1014 Curl_safefree(chlg);
1016 return CURLE_RECV_ERROR;
1019 /* Setup the "input" security buffer */
1020 input_desc.ulVersion = SECBUFFER_VERSION;
1021 input_desc.cBuffers = 2;
1022 input_desc.pBuffers = input_buf;
1023 input_buf[0].BufferType = SECBUFFER_STREAM;
1024 input_buf[0].pvBuffer = chlg;
1025 input_buf[0].cbBuffer = curlx_uztoul(chlglen);
1026 input_buf[1].BufferType = SECBUFFER_DATA;
1027 input_buf[1].pvBuffer = NULL;
1028 input_buf[1].cbBuffer = 0;
1030 /* Decrypt the inbound challenge and obtain the qop */
1031 status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
1032 if(status != SEC_E_OK) {
1033 infof(data, "GSSAPI handshake failure (empty security message)\n");
1035 Curl_safefree(chlg);
1037 return CURLE_BAD_CONTENT_ENCODING;
1040 /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
1041 if(input_buf[1].cbBuffer != 4) {
1042 infof(data, "GSSAPI handshake failure (invalid security data)\n");
1044 Curl_safefree(chlg);
1046 return CURLE_BAD_CONTENT_ENCODING;
1049 /* Copy the data out and free the challenge as it is not required anymore */
1050 memcpy(&indata, input_buf[1].pvBuffer, 4);
1051 s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
1052 Curl_safefree(chlg);
1054 /* Extract the security layer */
1055 sec_layer = indata & 0x000000FF;
1056 if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
1057 infof(data, "GSSAPI handshake failure (invalid security layer)\n");
1059 return CURLE_BAD_CONTENT_ENCODING;
1062 /* Extract the maximum message size the server can receive */
1063 max_size = ntohl(indata & 0xFFFFFF00);
1065 /* The server has told us it supports a maximum receive buffer, however, as
1066 we don't require one unless we are encrypting data, we tell the server
1067 our receive buffer is zero. */
1071 /* Allocate the trailer */
1072 trailer = malloc(sizes.cbSecurityTrailer);
1074 return CURLE_OUT_OF_MEMORY;
1076 /* Convert the user name to UTF8 when operating with Unicode */
1077 user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
1079 Curl_safefree(trailer);
1081 return CURLE_OUT_OF_MEMORY;
1084 /* Allocate our message */
1085 messagelen = sizeof(outdata) + strlen(user_name) + 1;
1086 message = malloc(messagelen);
1088 Curl_safefree(trailer);
1089 Curl_unicodefree(user_name);
1091 return CURLE_OUT_OF_MEMORY;
1094 /* Populate the message with the security layer, client supported receive
1095 message size and authorization identity including the 0x00 based
1096 terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization
1097 identity is not terminated with the zero-valued (%x00) octet." it seems
1098 necessary to include it. */
1099 outdata = htonl(max_size) | sec_layer;
1100 memcpy(message, &outdata, sizeof(outdata));
1101 strcpy((char *) message + sizeof(outdata), user_name);
1102 Curl_unicodefree(user_name);
1104 /* Allocate the padding */
1105 padding = malloc(sizes.cbBlockSize);
1107 Curl_safefree(message);
1108 Curl_safefree(trailer);
1110 return CURLE_OUT_OF_MEMORY;
1113 /* Setup the "authentication data" security buffer */
1114 wrap_desc.ulVersion = SECBUFFER_VERSION;
1115 wrap_desc.cBuffers = 3;
1116 wrap_desc.pBuffers = wrap_buf;
1117 wrap_buf[0].BufferType = SECBUFFER_TOKEN;
1118 wrap_buf[0].pvBuffer = trailer;
1119 wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
1120 wrap_buf[1].BufferType = SECBUFFER_DATA;
1121 wrap_buf[1].pvBuffer = message;
1122 wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
1123 wrap_buf[2].BufferType = SECBUFFER_PADDING;
1124 wrap_buf[2].pvBuffer = padding;
1125 wrap_buf[2].cbBuffer = sizes.cbBlockSize;
1127 /* Encrypt the data */
1128 status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
1130 if(status != SEC_E_OK) {
1131 Curl_safefree(padding);
1132 Curl_safefree(message);
1133 Curl_safefree(trailer);
1135 return CURLE_OUT_OF_MEMORY;
1138 /* Allocate the encryption (wrap) buffer */
1139 appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
1140 wrap_buf[2].cbBuffer;
1141 appdata = malloc(appdatalen);
1143 Curl_safefree(padding);
1144 Curl_safefree(message);
1145 Curl_safefree(trailer);
1147 return CURLE_OUT_OF_MEMORY;
1150 /* Populate the encryption buffer */
1151 memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
1152 offset += wrap_buf[0].cbBuffer;
1153 memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
1154 offset += wrap_buf[1].cbBuffer;
1155 memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
1157 /* Base64 encode the response */
1158 result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
1161 /* Free all of our local buffers */
1162 Curl_safefree(appdata);
1163 Curl_safefree(padding);
1164 Curl_safefree(message);
1165 Curl_safefree(trailer);
1171 * Curl_sasl_gssapi_cleanup()
1173 * This is used to clean up the gssapi specific data.
1177 * krb5 [in/out] - The kerberos 5 data struct being cleaned up.
1180 void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
1182 /* Free our security context */
1184 s_pSecFn->DeleteSecurityContext(krb5->context);
1185 free(krb5->context);
1186 krb5->context = NULL;
1189 /* Free our credentials handle */
1190 if(krb5->credentials) {
1191 s_pSecFn->FreeCredentialsHandle(krb5->credentials);
1192 free(krb5->credentials);
1193 krb5->credentials = NULL;
1196 /* Free our identity */
1197 Curl_sspi_free_identity(krb5->p_identity);
1198 krb5->p_identity = NULL;
1200 /* Free the SPN and output token */
1201 Curl_safefree(krb5->spn);
1202 Curl_safefree(krb5->output_token);
1204 /* Reset any variables */
1205 krb5->token_max = 0;
1207 #endif /* USE_KERBEROS5 */
1209 #endif /* USE_WINDOWS_SSPI */