Newer
Older
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014, Steve Holme, <steve_holme@hotmail.com>.
* Copyright (C) 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* RFC2617 Basic and Digest Access Authentication
* RFC2831 DIGEST-MD5 authentication
* RFC4422 Simple Authentication and Security Layer (SASL)
* RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI)
#include <curl/curl.h>
#include "urldata.h"
#include "curl_base64.h"
#include "warnless.h"
#include "curl_memory.h"
#include "curl_multibyte.h"
#include "sendf.h"
#include "strdup.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* The last #include file should be: */
#include "memdebug.h"
/*
* Curl_sasl_build_spn()
*
* This is used to build a SPN string in the format service/host.
*
* Parameters:
*
* serivce [in] - The service type such as www, smtp, pop or imap.
* host [in] - The host name or realm.
*
* Returns a pointer to the newly allocated SPN.
*/
TCHAR *Curl_sasl_build_spn(const char *service, const char *host)
{
char *utf8_spn = NULL;
TCHAR *tchar_spn = NULL;
/* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather
than doing this ourselves but the first is only available in Windows XP
and Windows Server 2003 and the latter is only available in Windows 2000
but not Windows95/98/ME or Windows NT4.0 unless the Active Directory
Client Extensions are installed. As such it is far simpler for us to
formulate the SPN instead. */
/* Allocate our UTF8 based SPN */
utf8_spn = aprintf("%s/%s", service, host);
if(!utf8_spn) {
return NULL;
}
/* Allocate our TCHAR based SPN */
tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn);
if(!tchar_spn) {
Curl_safefree(utf8_spn);
return NULL;
}
/* Release the UTF8 variant when operating with Unicode */
/* Return our newly allocated SPN */
return tchar_spn;
}
#if !defined(CURL_DISABLE_CRYPTO_AUTH)
/*
* Curl_sasl_create_digest_md5_message()
*
* This is used to generate an already encoded DIGEST-MD5 response message
* ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg64 [in] - Pointer to the base64 encoded challenge message.
* userp [in] - The user name.
* passdwp [in] - The user's password.
* service [in] - The service type such as www, smtp, pop or imap.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
const char *chlg64,
const char *userp,
const char *passwdp,
const char *service,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
TCHAR *spn = NULL;
size_t token_max = 0;
unsigned char *input_token = NULL;
unsigned char *output_token = NULL;
CredHandle credentials;
CtxtHandle context;
PSecPkgInfo SecurityPackage;
SEC_WINNT_AUTH_IDENTITY identity;
SEC_WINNT_AUTH_IDENTITY *p_identity;
SecBuffer chlg_buf;
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
/* Decode the base-64 encoded challenge message */
if(strlen(chlg64) && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &input_token, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!input_token) {
infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
/* Query the security package for DigestSSP */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
if(status != SEC_E_OK) {
Curl_safefree(input_token);
return CURLE_NOT_BUILT_IN;
}
token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our response buffer */
output_token = malloc(token_max);
if(!output_token) {
Curl_safefree(input_token);
return CURLE_OUT_OF_MEMORY;
}
/* Generate our SPN */
spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
if(!spn) {
Curl_safefree(output_token);
Curl_safefree(input_token);
return CURLE_OUT_OF_MEMORY;
if(userp && *userp) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &identity);
if(result) {
Curl_safefree(spn);
Curl_safefree(output_token);
Curl_safefree(input_token);
return result;
}
/* Allow proper cleanup of the identity structure */
p_identity = &identity;
else
/* Use the current Windows user */
p_identity = NULL;
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_DIGEST),
SECPKG_CRED_OUTBOUND, NULL,
p_identity, NULL, NULL,
if(status != SEC_E_OK) {
Curl_sspi_free_identity(p_identity);
Curl_safefree(output_token);
Curl_safefree(input_token);
return CURLE_LOGIN_DENIED;
/* Setup the challenge "input" security buffer */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf;
chlg_buf.BufferType = SECBUFFER_TOKEN;
chlg_buf.pvBuffer = input_token;
chlg_buf.cbBuffer = curlx_uztoul(chlglen);
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = output_token;
resp_buf.cbBuffer = curlx_uztoul(token_max);
/* Generate our response message */
status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
0, 0, 0, &chlg_desc, 0,
&context, &resp_desc, &attrs,
&expiry);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
Curl_safefree(output_token);
Curl_safefree(input_token);
return CURLE_RECV_ERROR;
}
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
outptr, outlen);
/* Free our handles */
s_pSecFn->DeleteSecurityContext(&context);
s_pSecFn->FreeCredentialsHandle(&credentials);
/* Free the identity structure */
Curl_sspi_free_identity(p_identity);
/* Free the SPN */
Curl_safefree(spn);
/* Free the response buffer */
Curl_safefree(output_token);
Curl_safefree(input_token);
/*
* Curl_sasl_decode_digest_http_message()
*
* This is used to decode a HTTP DIGEST challenge message into the seperate
* attributes.
*
* Parameters:
*
* chlg [in] - Pointer to the challenge message.
* digest [in/out] - The digest data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
struct digestdata *digest)
{
size_t chlglen = strlen(chlg);
/* We had an input token before and we got another one now. This means we
provided bad credentials in the previous request. */
if(digest->input_token)
return CURLE_BAD_CONTENT_ENCODING;
/* Simply store the challenge for use later */
digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
if(!digest->input_token)
return CURLE_OUT_OF_MEMORY;
digest->input_token_len = chlglen;
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
}
/*
* Curl_sasl_create_digest_http_message()
*
* This is used to generate a HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passdwp [in] - The user's password.
* request [in] - The HTTP request.
* uripath [in] - The path of the HTTP uri.
* digest [in/out] - The digest data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uripath,
struct digestdata *digest,
char **outptr, size_t *outlen)
{
size_t token_max;
CredHandle credentials;
CtxtHandle context;
char *resp;
BYTE *output_token;
PSecPkgInfo SecurityPackage;
SEC_WINNT_AUTH_IDENTITY identity;
SEC_WINNT_AUTH_IDENTITY *p_identity;
SecBuffer chlg_buf[3];
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
(void) data;
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/* Query the security package for DigestSSP */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
if(status != SEC_E_OK)
return CURLE_NOT_BUILT_IN;
token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate the output buffer according to the max token size as indicated
by the security package */
output_token = malloc(token_max);
if(!output_token)
return CURLE_OUT_OF_MEMORY;
if(userp && *userp) {
/* Populate our identity structure */
if(Curl_create_sspi_identity(userp, passwdp, &identity))
return CURLE_OUT_OF_MEMORY;
/* Allow proper cleanup of the identity structure */
p_identity = &identity;
}
else
/* Use the current Windows user */
p_identity = NULL;
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_DIGEST),
SECPKG_CRED_OUTBOUND, NULL,
p_identity, NULL, NULL,
&credentials, &expiry);
if(status != SEC_E_OK) {
Curl_safefree(output_token);
return CURLE_LOGIN_DENIED;
}
/* Setup the challenge "input" security buffer if present */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 3;
chlg_desc.pBuffers = chlg_buf;
chlg_buf[0].BufferType = SECBUFFER_TOKEN;
chlg_buf[0].pvBuffer = digest->input_token;
chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[1].pvBuffer = (void *)request;
chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[2].pvBuffer = NULL;
chlg_buf[2].cbBuffer = 0;
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = output_token;
resp_buf.cbBuffer = curlx_uztoul(token_max);
/* Generate our reponse message */
status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
(TCHAR *) uripath,
ISC_REQ_USE_HTTP_STYLE, 0, 0,
&chlg_desc, 0, &context,
&resp_desc, &attrs, &expiry);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_safefree(output_token);
return CURLE_OUT_OF_MEMORY;
}
resp = malloc(resp_buf.cbBuffer + 1);
if(!resp) {
s_pSecFn->DeleteSecurityContext(&context);
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_safefree(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Copy the generated reponse */
memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
resp[resp_buf.cbBuffer] = 0x00;
/* Return the response */
*outptr = resp;
*outlen = resp_buf.cbBuffer;
/* Free our handles */
s_pSecFn->DeleteSecurityContext(&context);
s_pSecFn->FreeCredentialsHandle(&credentials);
/* Free the identity structure */
Curl_sspi_free_identity(p_identity);
/* Free the response buffer */
Curl_safefree(output_token);
return CURLE_OK;
}
/*
* Curl_sasl_digest_cleanup()
*
* This is used to clean up the digest specific data.
*
* Parameters:
*
* digest [in/out] - The digest data struct being cleaned up.
*
*/
void Curl_sasl_digest_cleanup(struct digestdata *digest)
{
/* Free the input token */
Curl_safefree(digest->input_token);
/* Reset any variables */
digest->input_token_len = 0;
#endif /* !CURL_DISABLE_CRYPTO_AUTH */
/*
* Curl_sasl_create_ntlm_type1_message()
*
* This is used to generate an already encoded NTLM type-1 message ready for
* sending to the recipient.
*
* Note: This is a simple wrapper of the NTLM function which means that any
* SASL based protocols don't have to include the NTLM functions directly.
*
* Parameters:
*
* userp [in] - The user name in the format User or Domain\User.
* passdwp [in] - The user's password.
* ntlm [in/out] - The ntlm data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen)
{
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
PSecPkgInfo SecurityPackage;
SecBuffer type_1_buf;
SecBufferDesc type_1_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
/* Clean up any former leftovers and initialise to defaults */
Curl_sasl_ntlm_cleanup(ntlm);
/* Query the security package for NTLM */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
&SecurityPackage);
if(status != SEC_E_OK)
return CURLE_NOT_BUILT_IN;
ntlm->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our output buffer */
ntlm->output_token = malloc(ntlm->token_max);
if(!ntlm->output_token)
return CURLE_OUT_OF_MEMORY;
if(userp && *userp) {
CURLcode result;
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
if(result)
return result;
/* Allow proper cleanup of the identity structure */
ntlm->p_identity = &ntlm->identity;
}
else
/* Use the current Windows user */
ntlm->p_identity = NULL;
/* Allocate our credentials handle */
ntlm->credentials = malloc(sizeof(CredHandle));
if(!ntlm->credentials)
return CURLE_OUT_OF_MEMORY;
memset(ntlm->credentials, 0, sizeof(CredHandle));
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_NTLM),
SECPKG_CRED_OUTBOUND, NULL,
ntlm->p_identity, NULL, NULL,
ntlm->credentials, &expiry);
if(status != SEC_E_OK)
return CURLE_LOGIN_DENIED;
/* Allocate our new context handle */
ntlm->context = malloc(sizeof(CtxtHandle));
if(!ntlm->context)
return CURLE_OUT_OF_MEMORY;
memset(ntlm->context, 0, sizeof(CtxtHandle));
/* Setup the type-1 "output" security buffer */
type_1_desc.ulVersion = SECBUFFER_VERSION;
type_1_desc.cBuffers = 1;
type_1_desc.pBuffers = &type_1_buf;
type_1_buf.BufferType = SECBUFFER_TOKEN;
type_1_buf.pvBuffer = ntlm->output_token;
type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
/* Generate our type-1 message */
status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
(TCHAR *) TEXT(""),
0, 0, SECURITY_NETWORK_DREP,
NULL, 0,
ntlm->context, &type_1_desc,
&attrs, &expiry);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
return CURLE_RECV_ERROR;
/* Base64 encode the response */
return Curl_base64_encode(NULL, (char *) ntlm->output_token,
type_1_buf.cbBuffer, outptr, outlen);
}
/*
* Curl_sasl_decode_ntlm_type2_message()
*
* This is used to decode an already encoded NTLM type-2 message.
*
* Parameters:
*
* data [in] - Pointer to session handle.
* type2msg [in] - Pointer to the base64 encoded type-2 message.
* ntlm [in/out] - The ntlm data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
const char *type2msg,
struct ntlmdata *ntlm)
{
CURLcode result = CURLE_OK;
unsigned char *type2 = NULL;
size_t type2_len = 0;
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
/* Decode the base-64 encoded type-2 message */
if(strlen(type2msg) && *type2msg != '=') {
result = Curl_base64_decode(type2msg, &type2, &type2_len);
if(result)
return result;
}
/* Ensure we have a valid type-2 message */
if(!type2) {
infof(data, "NTLM handshake failure (empty type-2 message)\n");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Simply store the challenge for use later */
ntlm->input_token = type2;
ntlm->input_token_len = type2_len;
return result;
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
}
/*
* Curl_sasl_create_ntlm_type3_message()
*
* This is used to generate an already encoded NTLM type-3 message ready for
* sending to the recipient.
*
* Parameters:
*
* data [in] - Pointer to session handle.
* userp [in] - The user name in the format User or Domain\User.
* passdwp [in] - The user's password.
* ntlm [in/out] - The ntlm data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
char **outptr, size_t *outlen)
{
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
CURLcode result = CURLE_OK;
SecBuffer type_2_buf;
SecBuffer type_3_buf;
SecBufferDesc type_2_desc;
SecBufferDesc type_3_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
(void) passwdp;
(void) userp;
/* Setup the type-2 "input" security buffer */
type_2_desc.ulVersion = SECBUFFER_VERSION;
type_2_desc.cBuffers = 1;
type_2_desc.pBuffers = &type_2_buf;
type_2_buf.BufferType = SECBUFFER_TOKEN;
type_2_buf.pvBuffer = ntlm->input_token;
type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len);
/* Setup the type-3 "output" security buffer */
type_3_desc.ulVersion = SECBUFFER_VERSION;
type_3_desc.cBuffers = 1;
type_3_desc.pBuffers = &type_3_buf;
type_3_buf.BufferType = SECBUFFER_TOKEN;
type_3_buf.pvBuffer = ntlm->output_token;
type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
/* Generate our type-3 message */
status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
ntlm->context,
(TCHAR *) TEXT(""),
0, 0, SECURITY_NETWORK_DREP,
&type_2_desc,
0, ntlm->context,
&type_3_desc,
&attrs, &expiry);
if(status != SEC_E_OK) {
infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
status);
return CURLE_RECV_ERROR;
}
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *) ntlm->output_token,
type_3_buf.cbBuffer, outptr, outlen);
Curl_sasl_ntlm_cleanup(ntlm);
return result;
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
/*
* Curl_sasl_ntlm_cleanup()
*
* This is used to clean up the ntlm specific data.
*
* Parameters:
*
* ntlm [in/out] - The ntlm data struct being cleaned up.
*
*/
void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
{
/* Free our security context */
if(ntlm->context) {
s_pSecFn->DeleteSecurityContext(ntlm->context);
free(ntlm->context);
ntlm->context = NULL;
}
/* Free our credentials handle */
if(ntlm->credentials) {
s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
free(ntlm->credentials);
ntlm->credentials = NULL;
}
/* Free our identity */
Curl_sspi_free_identity(ntlm->p_identity);
ntlm->p_identity = NULL;
/* Free the input and output tokens */
Curl_safefree(ntlm->input_token);
Curl_safefree(ntlm->output_token);
/* Reset any variables */
ntlm->token_max = 0;
}
#endif /* USE_NTLM */
#if defined(USE_KERBEROS5)
/*
* Curl_sasl_create_gssapi_user_message()
*
* This is used to generate an already encoded GSSAPI (Kerberos V5) user token
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passdwp [in] - The user's password.
* service [in] - The service type such as www, smtp, pop or imap.
* mutual_auth [in] - Flag specifing whether or not mutual authentication
* is enabled.
* chlg64 [in] - Pointer to the optional base64 encoded challenge
* message.
* krb5 [in/out] - The gssapi data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
const char *userp,
const char *passwdp,
const char *service,
const bool mutual_auth,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
CtxtHandle context;
PSecPkgInfo SecurityPackage;
SecBuffer chlg_buf;
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
SECURITY_STATUS status;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
if(!krb5->credentials) {
/* Query the security package for Kerberos */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_KERBEROS),
&SecurityPackage);
if(status != SEC_E_OK) {
return CURLE_NOT_BUILT_IN;
}
krb5->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our response buffer */
krb5->output_token = malloc(krb5->token_max);
if(!krb5->output_token)
return CURLE_OUT_OF_MEMORY;
/* Generate our SPN */
krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
if(!krb5->spn)
return CURLE_OUT_OF_MEMORY;
if(userp && *userp) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
if(result)
return result;
/* Allow proper cleanup of the identity structure */
krb5->p_identity = &krb5->identity;
}
else
/* Use the current Windows user */
krb5->p_identity = NULL;
/* Allocate our credentials handle */
krb5->credentials = malloc(sizeof(CredHandle));
if(!krb5->credentials)
return CURLE_OUT_OF_MEMORY;
memset(krb5->credentials, 0, sizeof(CredHandle));
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)
TEXT(SP_NAME_KERBEROS),
SECPKG_CRED_OUTBOUND, NULL,
krb5->p_identity, NULL, NULL,
return CURLE_LOGIN_DENIED;
/* Allocate our new context handle */
krb5->context = malloc(sizeof(CtxtHandle));
if(!krb5->context)
return CURLE_OUT_OF_MEMORY;
memset(krb5->context, 0, sizeof(CtxtHandle));
}
else {
/* Decode the base-64 encoded challenge message */
if(strlen(chlg64) && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "GSSAPI handshake failure (empty challenge message)\n");
return CURLE_BAD_CONTENT_ENCODING;
/* Setup the challenge "input" security buffer */
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf;
chlg_buf.BufferType = SECBUFFER_TOKEN;
chlg_buf.pvBuffer = chlg;
chlg_buf.cbBuffer = curlx_uztoul(chlglen);
}
/* Setup the response "output" security buffer */
resp_desc.ulVersion = SECBUFFER_VERSION;
resp_desc.cBuffers = 1;
resp_desc.pBuffers = &resp_buf;
resp_buf.BufferType = SECBUFFER_TOKEN;
resp_buf.pvBuffer = krb5->output_token;
resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
/* Generate our challenge-response message */
status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
chlg ? krb5->context : NULL,
krb5->spn,
ISC_REQ_MUTUAL_AUTH : 0),
0, SECURITY_NATIVE_DREP,
chlg ? &chlg_desc : NULL, 0,
&context,
&resp_desc, &attrs,
if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
Curl_safefree(chlg);
}
if(memcmp(&context, krb5->context, sizeof(context))) {
s_pSecFn->DeleteSecurityContext(krb5->context);
memcpy(krb5->context, &context, sizeof(context));
}
if(resp_buf.cbBuffer) {
/* Base64 encode the response */
result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer,
resp_buf.cbBuffer, outptr, outlen);
}
/* Free the decoded challenge */
Curl_safefree(chlg);
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
return result;
}
/*
* Curl_sasl_create_gssapi_security_message()
*
* This is used to generate an already encoded GSSAPI (Kerberos V5) security
* token message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* chlg64 [in] - Pointer to the optional base64 encoded challenge message.
* krb5 [in/out] - The gssapi data struct being used and modified.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
const char *chlg64,
struct kerberos5data *krb5,
char **outptr,
size_t *outlen)
{
CURLcode result = CURLE_OK;
size_t offset = 0;
size_t chlglen = 0;
size_t messagelen = 0;
size_t appdatalen = 0;
unsigned char *chlg = NULL;
unsigned char *trailer = NULL;
unsigned char *message = NULL;
unsigned char *padding = NULL;
unsigned char *appdata = NULL;
SecBuffer input_buf[2];
SecBuffer wrap_buf[3];
SecBufferDesc input_desc;
SecBufferDesc wrap_desc;
unsigned long indata = 0;
unsigned long outdata = 0;
unsigned long qop = 0;
unsigned long sec_layer = 0;
unsigned long max_size = 0;
SecPkgContext_Sizes sizes;
SecPkgCredentials_Names names;
SECURITY_STATUS status;
/* Decode the base-64 encoded input message */
if(strlen(chlg64) && *chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "GSSAPI handshake failure (empty security message)\n");
return CURLE_BAD_CONTENT_ENCODING;