Add deps locally

This commit is contained in:
Rim
2023-12-11 20:30:44 -05:00
parent 5a888bbf06
commit 619e5550cc
5618 changed files with 1022726 additions and 6 deletions

138
deps/curl/lib/vauth/cleartext.c vendored Normal file
View File

@ -0,0 +1,138 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC4616 PLAIN authentication
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3) || \
(!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
#include "urldata.h"
#include "vauth/vauth.h"
#include "warnless.h"
#include "strtok.h"
#include "sendf.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_create_plain_message()
*
* This is used to generate an already encoded PLAIN message ready
* for sending to the recipient.
*
* Parameters:
*
* authzid [in] - The authorization identity.
* authcid [in] - The authentication identity.
* passwd [in] - The password.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_plain_message(const char *authzid,
const char *authcid,
const char *passwd,
struct bufref *out)
{
char *plainauth;
size_t plainlen;
size_t zlen;
size_t clen;
size_t plen;
zlen = (authzid == NULL ? 0 : strlen(authzid));
clen = strlen(authcid);
plen = strlen(passwd);
/* Compute binary message length. Check for overflows. */
if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) ||
(plen > (SIZE_T_MAX/2 - 2)))
return CURLE_OUT_OF_MEMORY;
plainlen = zlen + clen + plen + 2;
plainauth = malloc(plainlen + 1);
if(!plainauth)
return CURLE_OUT_OF_MEMORY;
/* Calculate the reply */
if(zlen)
memcpy(plainauth, authzid, zlen);
plainauth[zlen] = '\0';
memcpy(plainauth + zlen + 1, authcid, clen);
plainauth[zlen + clen + 1] = '\0';
memcpy(plainauth + zlen + clen + 2, passwd, plen);
plainauth[plainlen] = '\0';
Curl_bufref_set(out, plainauth, plainlen, curl_free);
return CURLE_OK;
}
/*
* Curl_auth_create_login_message()
*
* This is used to generate an already encoded LOGIN message containing the
* user name or password ready for sending to the recipient.
*
* Parameters:
*
* valuep [in] - The user name or user's password.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_login_message(const char *valuep, struct bufref *out)
{
Curl_bufref_set(out, valuep, strlen(valuep), NULL);
return CURLE_OK;
}
/*
* Curl_auth_create_external_message()
*
* This is used to generate an already encoded EXTERNAL message containing
* the user name ready for sending to the recipient.
*
* Parameters:
*
* user [in] - The user name.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_external_message(const char *user,
struct bufref *out)
{
/* This is the same formatting as the login message */
return Curl_auth_create_login_message(user, out);
}
#endif /* if no users */

97
deps/curl/lib/vauth/cram.c vendored Normal file
View File

@ -0,0 +1,97 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC2195 CRAM-MD5 authentication
*
***************************************************************************/
#include "curl_setup.h"
#ifndef CURL_DISABLE_DIGEST_AUTH
#include <curl/curl.h>
#include "urldata.h"
#include "vauth/vauth.h"
#include "curl_hmac.h"
#include "curl_md5.h"
#include "warnless.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_create_cram_md5_message()
*
* This is used to generate a CRAM-MD5 response message ready for sending to
* the recipient.
*
* Parameters:
*
* chlg [in] - The challenge.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg,
const char *userp,
const char *passwdp,
struct bufref *out)
{
struct HMAC_context *ctxt;
unsigned char digest[MD5_DIGEST_LEN];
char *response;
/* Compute the digest using the password as the key */
ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
(const unsigned char *) passwdp,
curlx_uztoui(strlen(passwdp)));
if(!ctxt)
return CURLE_OUT_OF_MEMORY;
/* Update the digest with the given challenge */
if(Curl_bufref_len(chlg))
Curl_HMAC_update(ctxt, Curl_bufref_ptr(chlg),
curlx_uztoui(Curl_bufref_len(chlg)));
/* Finalise the digest */
Curl_HMAC_final(ctxt, digest);
/* Generate the response */
response = aprintf(
"%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
userp, digest[0], digest[1], digest[2], digest[3], digest[4],
digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
digest[11], digest[12], digest[13], digest[14], digest[15]);
if(!response)
return CURLE_OUT_OF_MEMORY;
Curl_bufref_set(out, response, strlen(response), curl_free);
return CURLE_OK;
}
#endif /* !CURL_DISABLE_DIGEST_AUTH */

994
deps/curl/lib/vauth/digest.c vendored Normal file
View File

@ -0,0 +1,994 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC2831 DIGEST-MD5 authentication
* RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
*
***************************************************************************/
#include "curl_setup.h"
#ifndef CURL_DISABLE_DIGEST_AUTH
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "vauth/digest.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_hmac.h"
#include "curl_md5.h"
#include "curl_sha256.h"
#include "vtls/vtls.h"
#include "warnless.h"
#include "strtok.h"
#include "strcase.h"
#include "curl_printf.h"
#include "rand.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
#define SESSION_ALGO 1 /* for algos with this bit set */
#define ALGO_MD5 0
#define ALGO_MD5SESS (ALGO_MD5 | SESSION_ALGO)
#define ALGO_SHA256 2
#define ALGO_SHA256SESS (ALGO_SHA256 | SESSION_ALGO)
#define ALGO_SHA512_256 4
#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO)
#if !defined(USE_WINDOWS_SSPI)
#define DIGEST_QOP_VALUE_AUTH (1 << 0)
#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
#define DIGEST_QOP_VALUE_STRING_AUTH "auth"
#define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int"
#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
#endif
bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
const char **endptr)
{
int c;
bool starts_with_quote = FALSE;
bool escape = FALSE;
for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
*value++ = *str++;
*value = 0;
if('=' != *str++)
/* eek, no match */
return FALSE;
if('\"' == *str) {
/* This starts with a quote so it must end with one as well! */
str++;
starts_with_quote = TRUE;
}
for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
if(!escape) {
switch(*str) {
case '\\':
if(starts_with_quote) {
/* the start of an escaped quote */
escape = TRUE;
continue;
}
break;
case ',':
if(!starts_with_quote) {
/* This signals the end of the content if we didn't get a starting
quote and then we do "sloppy" parsing */
c = 0; /* the end */
continue;
}
break;
case '\r':
case '\n':
/* end of string */
if(starts_with_quote)
return FALSE; /* No closing quote */
c = 0;
continue;
case '\"':
if(starts_with_quote) {
/* end of string */
c = 0;
continue;
}
else
return FALSE;
}
}
escape = FALSE;
*content++ = *str;
}
if(escape)
return FALSE; /* No character after backslash */
*content = 0;
*endptr = str;
return TRUE;
}
#if !defined(USE_WINDOWS_SSPI)
/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */
static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
unsigned char *dest) /* 33 bytes */
{
int i;
for(i = 0; i < 16; i++)
msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
/* Convert sha256 chunk to RFC7616 -suitable ascii string */
static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
unsigned char *dest) /* 65 bytes */
{
int i;
for(i = 0; i < 32; i++)
msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
/* Perform quoted-string escaping as described in RFC2616 and its errata */
static char *auth_digest_string_quoted(const char *source)
{
char *dest;
const char *s = source;
size_t n = 1; /* null terminator */
/* Calculate size needed */
while(*s) {
++n;
if(*s == '"' || *s == '\\') {
++n;
}
++s;
}
dest = malloc(n);
if(dest) {
char *d = dest;
s = source;
while(*s) {
if(*s == '"' || *s == '\\') {
*d++ = '\\';
}
*d++ = *s++;
}
*d = '\0';
}
return dest;
}
/* Retrieves the value for a corresponding key from the challenge string
* returns TRUE if the key could be found, FALSE if it does not exists
*/
static bool auth_digest_get_key_value(const char *chlg,
const char *key,
char *value,
size_t max_val_len,
char end_char)
{
char *find_pos;
size_t i;
find_pos = strstr(chlg, key);
if(!find_pos)
return FALSE;
find_pos += strlen(key);
for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
value[i] = *find_pos++;
value[i] = '\0';
return TRUE;
}
static CURLcode auth_digest_get_qop_values(const char *options, int *value)
{
char *tmp;
char *token;
char *tok_buf = NULL;
/* Initialise the output */
*value = 0;
/* Tokenise the list of qop values. Use a temporary clone of the buffer since
strtok_r() ruins it. */
tmp = strdup(options);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
token = strtok_r(tmp, ",", &tok_buf);
while(token) {
if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
*value |= DIGEST_QOP_VALUE_AUTH;
else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
*value |= DIGEST_QOP_VALUE_AUTH_INT;
else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
*value |= DIGEST_QOP_VALUE_AUTH_CONF;
token = strtok_r(NULL, ",", &tok_buf);
}
free(tmp);
return CURLE_OK;
}
/*
* auth_decode_digest_md5_message()
*
* This is used internally to decode an already encoded DIGEST-MD5 challenge
* message into the separate attributes.
*
* Parameters:
*
* chlgref [in] - The challenge message.
* nonce [in/out] - The buffer where the nonce will be stored.
* nlen [in] - The length of the nonce buffer.
* realm [in/out] - The buffer where the realm will be stored.
* rlen [in] - The length of the realm buffer.
* alg [in/out] - The buffer where the algorithm will be stored.
* alen [in] - The length of the algorithm buffer.
* qop [in/out] - The buffer where the qop-options will be stored.
* qlen [in] - The length of the qop buffer.
*
* Returns CURLE_OK on success.
*/
static CURLcode auth_decode_digest_md5_message(const struct bufref *chlgref,
char *nonce, size_t nlen,
char *realm, size_t rlen,
char *alg, size_t alen,
char *qop, size_t qlen)
{
const char *chlg = (const char *) Curl_bufref_ptr(chlgref);
/* Ensure we have a valid challenge message */
if(!Curl_bufref_len(chlgref))
return CURLE_BAD_CONTENT_ENCODING;
/* Retrieve nonce string from the challenge */
if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"'))
return CURLE_BAD_CONTENT_ENCODING;
/* Retrieve realm string from the challenge */
if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) {
/* Challenge does not have a realm, set empty string [RFC2831] page 6 */
strcpy(realm, "");
}
/* Retrieve algorithm string from the challenge */
if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ','))
return CURLE_BAD_CONTENT_ENCODING;
/* Retrieve qop-options string from the challenge */
if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"'))
return CURLE_BAD_CONTENT_ENCODING;
return CURLE_OK;
}
/*
* Curl_auth_is_digest_supported()
*
* This is used to evaluate if DIGEST is supported.
*
* Parameters: None
*
* Returns TRUE as DIGEST as handled by libcurl.
*/
bool Curl_auth_is_digest_supported(void)
{
return TRUE;
}
/*
* Curl_auth_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.
* chlg [in] - The challenge message.
* userp [in] - The user name.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
const struct bufref *chlg,
const char *userp,
const char *passwdp,
const char *service,
struct bufref *out)
{
size_t i;
struct MD5_context *ctxt;
char *response = NULL;
unsigned char digest[MD5_DIGEST_LEN];
char HA1_hex[2 * MD5_DIGEST_LEN + 1];
char HA2_hex[2 * MD5_DIGEST_LEN + 1];
char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
char nonce[64];
char realm[128];
char algorithm[64];
char qop_options[64];
int qop_values;
char cnonce[33];
char nonceCount[] = "00000001";
char method[] = "AUTHENTICATE";
char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
char *spn = NULL;
/* Decode the challenge message */
CURLcode result = auth_decode_digest_md5_message(chlg,
nonce, sizeof(nonce),
realm, sizeof(realm),
algorithm,
sizeof(algorithm),
qop_options,
sizeof(qop_options));
if(result)
return result;
/* We only support md5 sessions */
if(strcmp(algorithm, "md5-sess") != 0)
return CURLE_BAD_CONTENT_ENCODING;
/* Get the qop-values from the qop-options */
result = auth_digest_get_qop_values(qop_options, &qop_values);
if(result)
return result;
/* We only support auth quality-of-protection */
if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
return CURLE_BAD_CONTENT_ENCODING;
/* Generate 32 random hex chars, 32 bytes + 1 null-termination */
result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
if(result)
return result;
/* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt)
return CURLE_OUT_OF_MEMORY;
Curl_MD5_update(ctxt, (const unsigned char *) userp,
curlx_uztoui(strlen(userp)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) realm,
curlx_uztoui(strlen(realm)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
curlx_uztoui(strlen(passwdp)));
Curl_MD5_final(ctxt, digest);
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt)
return CURLE_OUT_OF_MEMORY;
Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) nonce,
curlx_uztoui(strlen(nonce)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
curlx_uztoui(strlen(cnonce)));
Curl_MD5_final(ctxt, digest);
/* Convert calculated 16 octet hex into 32 bytes string */
for(i = 0; i < MD5_DIGEST_LEN; i++)
msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
/* Generate our SPN */
spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
if(!spn)
return CURLE_OUT_OF_MEMORY;
/* Calculate H(A2) */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt) {
free(spn);
return CURLE_OUT_OF_MEMORY;
}
Curl_MD5_update(ctxt, (const unsigned char *) method,
curlx_uztoui(strlen(method)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) spn,
curlx_uztoui(strlen(spn)));
Curl_MD5_final(ctxt, digest);
for(i = 0; i < MD5_DIGEST_LEN; i++)
msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
/* Now calculate the response hash */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt) {
free(spn);
return CURLE_OUT_OF_MEMORY;
}
Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) nonce,
curlx_uztoui(strlen(nonce)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
curlx_uztoui(strlen(nonceCount)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
curlx_uztoui(strlen(cnonce)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) qop,
curlx_uztoui(strlen(qop)));
Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
Curl_MD5_final(ctxt, digest);
for(i = 0; i < MD5_DIGEST_LEN; i++)
msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
/* Generate the response */
response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
"cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
"qop=%s",
userp, realm, nonce,
cnonce, nonceCount, spn, resp_hash_hex, qop);
free(spn);
if(!response)
return CURLE_OUT_OF_MEMORY;
/* Return the response. */
Curl_bufref_set(out, response, strlen(response), curl_free);
return result;
}
/*
* Curl_auth_decode_digest_http_message()
*
* This is used to decode an HTTP DIGEST challenge message into the separate
* attributes.
*
* Parameters:
*
* chlg [in] - The challenge message.
* digest [in/out] - The digest data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest)
{
bool before = FALSE; /* got a nonce before */
bool foundAuth = FALSE;
bool foundAuthInt = FALSE;
char *token = NULL;
char *tmp = NULL;
/* If we already have received a nonce, keep that in mind */
if(digest->nonce)
before = TRUE;
/* Clean up any former leftovers and initialise to defaults */
Curl_auth_digest_cleanup(digest);
for(;;) {
char value[DIGEST_MAX_VALUE_LENGTH];
char content[DIGEST_MAX_CONTENT_LENGTH];
/* Pass all additional spaces here */
while(*chlg && ISBLANK(*chlg))
chlg++;
/* Extract a value=content pair */
if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
if(strcasecompare(value, "nonce")) {
free(digest->nonce);
digest->nonce = strdup(content);
if(!digest->nonce)
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "stale")) {
if(strcasecompare(content, "true")) {
digest->stale = TRUE;
digest->nc = 1; /* we make a new nonce now */
}
}
else if(strcasecompare(value, "realm")) {
free(digest->realm);
digest->realm = strdup(content);
if(!digest->realm)
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "opaque")) {
free(digest->opaque);
digest->opaque = strdup(content);
if(!digest->opaque)
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "qop")) {
char *tok_buf = NULL;
/* Tokenize the list and choose auth if possible, use a temporary
clone of the buffer since strtok_r() ruins it */
tmp = strdup(content);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
token = strtok_r(tmp, ",", &tok_buf);
while(token) {
/* Pass additional spaces here */
while(*token && ISBLANK(*token))
token++;
if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
foundAuth = TRUE;
}
else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
foundAuthInt = TRUE;
}
token = strtok_r(NULL, ",", &tok_buf);
}
free(tmp);
/* Select only auth or auth-int. Otherwise, ignore */
if(foundAuth) {
free(digest->qop);
digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
if(!digest->qop)
return CURLE_OUT_OF_MEMORY;
}
else if(foundAuthInt) {
free(digest->qop);
digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
if(!digest->qop)
return CURLE_OUT_OF_MEMORY;
}
}
else if(strcasecompare(value, "algorithm")) {
free(digest->algorithm);
digest->algorithm = strdup(content);
if(!digest->algorithm)
return CURLE_OUT_OF_MEMORY;
if(strcasecompare(content, "MD5-sess"))
digest->algo = ALGO_MD5SESS;
else if(strcasecompare(content, "MD5"))
digest->algo = ALGO_MD5;
else if(strcasecompare(content, "SHA-256"))
digest->algo = ALGO_SHA256;
else if(strcasecompare(content, "SHA-256-SESS"))
digest->algo = ALGO_SHA256SESS;
else if(strcasecompare(content, "SHA-512-256"))
digest->algo = ALGO_SHA512_256;
else if(strcasecompare(content, "SHA-512-256-SESS"))
digest->algo = ALGO_SHA512_256SESS;
else
return CURLE_BAD_CONTENT_ENCODING;
}
else if(strcasecompare(value, "userhash")) {
if(strcasecompare(content, "true")) {
digest->userhash = TRUE;
}
}
else {
/* Unknown specifier, ignore it! */
}
}
else
break; /* We're done here */
/* Pass all additional spaces here */
while(*chlg && ISBLANK(*chlg))
chlg++;
/* Allow the list to be comma-separated */
if(',' == *chlg)
chlg++;
}
/* We had a nonce since before, and we got another one now without
'stale=true'. This means we provided bad credentials in the previous
request */
if(before && !digest->stale)
return CURLE_BAD_CONTENT_ENCODING;
/* We got this header without a nonce, that's a bad Digest line! */
if(!digest->nonce)
return CURLE_BAD_CONTENT_ENCODING;
/* "<algo>-sess" protocol versions require "auth" or "auth-int" qop */
if(!digest->qop && (digest->algo & SESSION_ALGO))
return CURLE_BAD_CONTENT_ENCODING;
return CURLE_OK;
}
/*
* auth_create_digest_http_message()
*
* This is used to generate an HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passwdp [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.
*/
static CURLcode auth_create_digest_http_message(
struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uripath,
struct digestdata *digest,
char **outptr, size_t *outlen,
void (*convert_to_ascii)(unsigned char *, unsigned char *),
CURLcode (*hash)(unsigned char *, const unsigned char *,
const size_t))
{
CURLcode result;
unsigned char hashbuf[32]; /* 32 bytes/256 bits */
unsigned char request_digest[65];
unsigned char ha1[65]; /* 64 digits and 1 zero byte */
unsigned char ha2[65]; /* 64 digits and 1 zero byte */
char userh[65];
char *cnonce = NULL;
size_t cnonce_sz = 0;
char *userp_quoted;
char *realm_quoted;
char *nonce_quoted;
char *response = NULL;
char *hashthis = NULL;
char *tmp = NULL;
memset(hashbuf, 0, sizeof(hashbuf));
if(!digest->nc)
digest->nc = 1;
if(!digest->cnonce) {
char cnoncebuf[33];
result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
sizeof(cnoncebuf));
if(result)
return result;
result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf),
&cnonce, &cnonce_sz);
if(result)
return result;
digest->cnonce = cnonce;
}
if(digest->userhash) {
hashthis = aprintf("%s:%s", userp, digest->realm ? digest->realm : "");
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, (unsigned char *)userh);
}
/*
If the algorithm is "MD5" or unspecified (which then defaults to MD5):
A1 = unq(username-value) ":" unq(realm-value) ":" passwd
If the algorithm is "MD5-sess" then:
A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
unq(nonce-value) ":" unq(cnonce-value)
*/
hashthis = aprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "",
passwdp);
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, ha1);
if(digest->algo & SESSION_ALGO) {
/* nonce and cnonce are OUTSIDE the hash */
tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
free(tmp);
convert_to_ascii(hashbuf, ha1);
}
/*
If the "qop" directive's value is "auth" or is unspecified, then A2 is:
A2 = Method ":" digest-uri-value
If the "qop" value is "auth-int", then A2 is:
A2 = Method ":" digest-uri-value ":" H(entity-body)
(The "Method" value is the HTTP request method as specified in section
5.1.1 of RFC 2616)
*/
hashthis = aprintf("%s:%s", request, uripath);
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
/* We don't support auth-int for PUT or POST */
char hashed[65];
char *hashthis2;
hash(hashbuf, (const unsigned char *)"", 0);
convert_to_ascii(hashbuf, (unsigned char *)hashed);
hashthis2 = aprintf("%s:%s", hashthis, hashed);
free(hashthis);
hashthis = hashthis2;
}
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, ha2);
if(digest->qop) {
hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
digest->cnonce, digest->qop, ha2);
}
else {
hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
}
if(!hashthis)
return CURLE_OUT_OF_MEMORY;
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
free(hashthis);
convert_to_ascii(hashbuf, request_digest);
/* For test case 64 (snooped from a Mozilla 1.3a request)
Authorization: Digest username="testuser", realm="testrealm", \
nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
Digest parameters are all quoted strings. Username which is provided by
the user will need double quotes and backslashes within it escaped.
realm, nonce, and opaque will need backslashes as well as they were
de-escaped when copied from request header. cnonce is generated with
web-safe characters. uri is already percent encoded. nc is 8 hex
characters. algorithm and qop with standard values only contain web-safe
characters.
*/
userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
if(!userp_quoted)
return CURLE_OUT_OF_MEMORY;
if(digest->realm)
realm_quoted = auth_digest_string_quoted(digest->realm);
else {
realm_quoted = malloc(1);
if(realm_quoted)
realm_quoted[0] = 0;
}
if(!realm_quoted) {
free(userp_quoted);
return CURLE_OUT_OF_MEMORY;
}
nonce_quoted = auth_digest_string_quoted(digest->nonce);
if(!nonce_quoted) {
free(realm_quoted);
free(userp_quoted);
return CURLE_OUT_OF_MEMORY;
}
if(digest->qop) {
response = aprintf("username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"cnonce=\"%s\", "
"nc=%08x, "
"qop=%s, "
"response=\"%s\"",
userp_quoted,
realm_quoted,
nonce_quoted,
uripath,
digest->cnonce,
digest->nc,
digest->qop,
request_digest);
/* Increment nonce-count to use another nc value for the next request */
digest->nc++;
}
else {
response = aprintf("username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"response=\"%s\"",
userp_quoted,
realm_quoted,
nonce_quoted,
uripath,
request_digest);
}
free(nonce_quoted);
free(realm_quoted);
free(userp_quoted);
if(!response)
return CURLE_OUT_OF_MEMORY;
/* Add the optional fields */
if(digest->opaque) {
char *opaque_quoted;
/* Append the opaque */
opaque_quoted = auth_digest_string_quoted(digest->opaque);
if(!opaque_quoted) {
free(response);
return CURLE_OUT_OF_MEMORY;
}
tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted);
free(response);
free(opaque_quoted);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
response = tmp;
}
if(digest->algorithm) {
/* Append the algorithm */
tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
free(response);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
response = tmp;
}
if(digest->userhash) {
/* Append the userhash */
tmp = aprintf("%s, userhash=true", response);
free(response);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
response = tmp;
}
/* Return the output */
*outptr = response;
*outlen = strlen(response);
return CURLE_OK;
}
/*
* Curl_auth_create_digest_http_message()
*
* This is used to generate an HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name.
* passwdp [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_auth_create_digest_http_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uripath,
struct digestdata *digest,
char **outptr, size_t *outlen)
{
if(digest->algo <= ALGO_MD5SESS)
return auth_create_digest_http_message(data, userp, passwdp,
request, uripath, digest,
outptr, outlen,
auth_digest_md5_to_ascii,
Curl_md5it);
DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS);
return auth_create_digest_http_message(data, userp, passwdp,
request, uripath, digest,
outptr, outlen,
auth_digest_sha256_to_ascii,
Curl_sha256it);
}
/*
* Curl_auth_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_auth_digest_cleanup(struct digestdata *digest)
{
Curl_safefree(digest->nonce);
Curl_safefree(digest->cnonce);
Curl_safefree(digest->realm);
Curl_safefree(digest->opaque);
Curl_safefree(digest->qop);
Curl_safefree(digest->algorithm);
digest->nc = 0;
digest->algo = ALGO_MD5; /* default algorithm */
digest->stale = FALSE; /* default means normal, not stale */
digest->userhash = FALSE;
}
#endif /* !USE_WINDOWS_SSPI */
#endif /* !CURL_DISABLE_DIGEST_AUTH */

40
deps/curl/lib/vauth/digest.h vendored Normal file
View File

@ -0,0 +1,40 @@
#ifndef HEADER_CURL_DIGEST_H
#define HEADER_CURL_DIGEST_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include <curl/curl.h>
#ifndef CURL_DISABLE_DIGEST_AUTH
#define DIGEST_MAX_VALUE_LENGTH 256
#define DIGEST_MAX_CONTENT_LENGTH 1024
/* This is used to extract the realm from a challenge message */
bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
const char **endptr);
#endif
#endif /* HEADER_CURL_DIGEST_H */

668
deps/curl/lib/vauth/digest_sspi.c vendored Normal file
View File

@ -0,0 +1,668 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC2831 DIGEST-MD5 authentication
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "vauth/digest.h"
#include "urldata.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
#include "strdup.h"
#include "strcase.h"
#include "strerror.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_digest_supported()
*
* This is used to evaluate if DIGEST is supported.
*
* Parameters: None
*
* Returns TRUE if DIGEST is supported by Windows SSPI.
*/
bool Curl_auth_is_digest_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for Digest */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_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.
* chlg [in] - The challenge message.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
const struct bufref *chlg,
const char *userp,
const char *passwdp,
const char *service,
struct bufref *out)
{
CURLcode result = CURLE_OK;
TCHAR *spn = NULL;
size_t token_max = 0;
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 */
/* Ensure we have a valid challenge message */
if(!Curl_bufref_len(chlg)) {
infof(data, "DIGEST-MD5 handshake failure (empty challenge message)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Query the security package for DigestSSP */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
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)
return CURLE_OUT_OF_MEMORY;
/* Generate our SPN */
spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
if(!spn) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
if(userp && *userp) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(userp, passwdp, &identity);
if(result) {
free(spn);
free(output_token);
return result;
}
/* 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_sspi_free_identity(p_identity);
free(spn);
free(output_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 = (void *) Curl_bufref_ptr(chlg);
chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
/* 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) {
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
char buffer[STRERROR_LEN];
#endif
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(spn);
free(output_token);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
infof(data, "schannel: InitializeSecurityContext failed: %s",
Curl_sspi_strerror(status, buffer, sizeof(buffer)));
return CURLE_AUTH_ERROR;
}
/* Return the response. */
Curl_bufref_set(out, output_token, resp_buf.cbBuffer, curl_free);
/* Free our handles */
s_pSecFn->DeleteSecurityContext(&context);
s_pSecFn->FreeCredentialsHandle(&credentials);
/* Free the identity structure */
Curl_sspi_free_identity(p_identity);
/* Free the SPN */
free(spn);
return result;
}
/*
* Curl_override_sspi_http_realm()
*
* This is used to populate the domain in a SSPI identity structure
* The realm is extracted from the challenge message and used as the
* domain if it is not already explicitly set.
*
* Parameters:
*
* chlg [in] - The challenge message.
* identity [in/out] - The identity structure.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_override_sspi_http_realm(const char *chlg,
SEC_WINNT_AUTH_IDENTITY *identity)
{
xcharp_u domain, dup_domain;
/* If domain is blank or unset, check challenge message for realm */
if(!identity->Domain || !identity->DomainLength) {
for(;;) {
char value[DIGEST_MAX_VALUE_LENGTH];
char content[DIGEST_MAX_CONTENT_LENGTH];
/* Pass all additional spaces here */
while(*chlg && ISBLANK(*chlg))
chlg++;
/* Extract a value=content pair */
if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
if(strcasecompare(value, "realm")) {
/* Setup identity's domain and length */
domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content);
if(!domain.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
if(!dup_domain.tchar_ptr) {
curlx_unicodefree(domain.tchar_ptr);
return CURLE_OUT_OF_MEMORY;
}
free(identity->Domain);
identity->Domain = dup_domain.tbyte_ptr;
identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
dup_domain.tchar_ptr = NULL;
curlx_unicodefree(domain.tchar_ptr);
}
else {
/* Unknown specifier, ignore it! */
}
}
else
break; /* We're done here */
/* Pass all additional spaces here */
while(*chlg && ISBLANK(*chlg))
chlg++;
/* Allow the list to be comma-separated */
if(',' == *chlg)
chlg++;
}
}
return CURLE_OK;
}
/*
* Curl_auth_decode_digest_http_message()
*
* This is used to decode an HTTP DIGEST challenge message into the separate
* attributes.
*
* Parameters:
*
* chlg [in] - The challenge message.
* digest [in/out] - The digest data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest)
{
size_t chlglen = strlen(chlg);
/* We had an input token before so if there's another one now that means we
provided bad credentials in the previous request or it's stale. */
if(digest->input_token) {
bool stale = false;
const char *p = chlg;
/* Check for the 'stale' directive */
for(;;) {
char value[DIGEST_MAX_VALUE_LENGTH];
char content[DIGEST_MAX_CONTENT_LENGTH];
while(*p && ISBLANK(*p))
p++;
if(!Curl_auth_digest_get_pair(p, value, content, &p))
break;
if(strcasecompare(value, "stale") &&
strcasecompare(content, "true")) {
stale = true;
break;
}
while(*p && ISBLANK(*p))
p++;
if(',' == *p)
p++;
}
if(stale)
Curl_auth_digest_cleanup(digest);
else
return CURLE_LOGIN_DENIED;
}
/* Store the challenge for use later */
digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1);
if(!digest->input_token)
return CURLE_OUT_OF_MEMORY;
digest->input_token_len = chlglen;
return CURLE_OK;
}
/*
* Curl_auth_create_digest_http_message()
*
* This is used to generate an HTTP DIGEST response message ready for sending
* to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [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_auth_create_digest_http_message(struct Curl_easy *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;
char *resp;
BYTE *output_token;
size_t output_token_len = 0;
PSecPkgInfo SecurityPackage;
SecBuffer chlg_buf[5];
SecBufferDesc chlg_desc;
SECURITY_STATUS status;
(void) data;
/* Query the security package for DigestSSP */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
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 the user/passwd that was used to make the identity for http_context
has changed then delete that context. */
if((userp && !digest->user) || (!userp && digest->user) ||
(passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
(userp && digest->user && Curl_timestrcmp(userp, digest->user)) ||
(passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) {
if(digest->http_context) {
s_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
Curl_safefree(digest->user);
Curl_safefree(digest->passwd);
}
if(digest->http_context) {
chlg_desc.ulVersion = SECBUFFER_VERSION;
chlg_desc.cBuffers = 5;
chlg_desc.pBuffers = chlg_buf;
chlg_buf[0].BufferType = SECBUFFER_TOKEN;
chlg_buf[0].pvBuffer = NULL;
chlg_buf[0].cbBuffer = 0;
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 = (void *) uripath;
chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath));
chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[3].pvBuffer = NULL;
chlg_buf[3].cbBuffer = 0;
chlg_buf[4].BufferType = SECBUFFER_PADDING;
chlg_buf[4].pvBuffer = output_token;
chlg_buf[4].cbBuffer = curlx_uztoul(token_max);
status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0);
if(status == SEC_E_OK)
output_token_len = chlg_buf[4].cbBuffer;
else { /* delete the context so a new one can be made */
infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx",
(long)status);
s_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
}
if(!digest->http_context) {
CredHandle credentials;
SEC_WINNT_AUTH_IDENTITY identity;
SEC_WINNT_AUTH_IDENTITY *p_identity;
SecBuffer resp_buf;
SecBufferDesc resp_desc;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
TCHAR *spn;
/* free the copy of user/passwd used to make the previous identity */
Curl_safefree(digest->user);
Curl_safefree(digest->passwd);
if(userp && *userp) {
/* Populate our identity structure */
if(Curl_create_sspi_identity(userp, passwdp, &identity)) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Populate our identity domain */
if(Curl_override_sspi_http_realm((const char *) digest->input_token,
&identity)) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Allow proper cleanup of the identity structure */
p_identity = &identity;
}
else
/* Use the current Windows user */
p_identity = NULL;
if(userp) {
digest->user = strdup(userp);
if(!digest->user) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
}
if(passwdp) {
digest->passwd = strdup(passwdp);
if(!digest->passwd) {
free(output_token);
Curl_safefree(digest->user);
return CURLE_OUT_OF_MEMORY;
}
}
/* 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_sspi_free_identity(p_identity);
free(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);
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);
spn = curlx_convert_UTF8_to_tchar((char *) uripath);
if(!spn) {
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Allocate our new context handle */
digest->http_context = calloc(1, sizeof(CtxtHandle));
if(!digest->http_context)
return CURLE_OUT_OF_MEMORY;
/* Generate our response message */
status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
spn,
ISC_REQ_USE_HTTP_STYLE, 0, 0,
&chlg_desc, 0,
digest->http_context,
&resp_desc, &attrs, &expiry);
curlx_unicodefree(spn);
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) {
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
char buffer[STRERROR_LEN];
#endif
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(output_token);
Curl_safefree(digest->http_context);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
infof(data, "schannel: InitializeSecurityContext failed: %s",
Curl_sspi_strerror(status, buffer, sizeof(buffer)));
return CURLE_AUTH_ERROR;
}
output_token_len = resp_buf.cbBuffer;
s_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
}
resp = malloc(output_token_len + 1);
if(!resp) {
free(output_token);
return CURLE_OUT_OF_MEMORY;
}
/* Copy the generated response */
memcpy(resp, output_token, output_token_len);
resp[output_token_len] = 0;
/* Return the response */
*outptr = resp;
*outlen = output_token_len;
/* Free the response buffer */
free(output_token);
return CURLE_OK;
}
/*
* Curl_auth_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_auth_digest_cleanup(struct digestdata *digest)
{
/* Free the input token */
Curl_safefree(digest->input_token);
/* Reset any variables */
digest->input_token_len = 0;
/* Delete security context */
if(digest->http_context) {
s_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
/* Free the copy of user/passwd used to make the identity for http_context */
Curl_safefree(digest->user);
Curl_safefree(digest->passwd);
}
#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */

127
deps/curl/lib/vauth/gsasl.c vendored Normal file
View File

@ -0,0 +1,127 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Simon Josefsson, <simon@josefsson.org>, 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC5802 SCRAM-SHA-1 authentication
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_GSASL
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "sendf.h"
#include <gsasl.h>
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
const char *mech,
struct gsasldata *gsasl)
{
int res;
res = gsasl_init(&gsasl->ctx);
if(res != GSASL_OK) {
failf(data, "gsasl init: %s\n", gsasl_strerror(res));
return FALSE;
}
res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client);
if(res != GSASL_OK) {
gsasl_done(gsasl->ctx);
return FALSE;
}
return true;
}
CURLcode Curl_auth_gsasl_start(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct gsasldata *gsasl)
{
#if GSASL_VERSION_NUMBER >= 0x010b00
int res;
res =
#endif
gsasl_property_set(gsasl->client, GSASL_AUTHID, userp);
#if GSASL_VERSION_NUMBER >= 0x010b00
if(res != GSASL_OK) {
failf(data, "setting AUTHID failed: %s\n", gsasl_strerror(res));
return CURLE_OUT_OF_MEMORY;
}
#endif
#if GSASL_VERSION_NUMBER >= 0x010b00
res =
#endif
gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp);
#if GSASL_VERSION_NUMBER >= 0x010b00
if(res != GSASL_OK) {
failf(data, "setting PASSWORD failed: %s\n", gsasl_strerror(res));
return CURLE_OUT_OF_MEMORY;
}
#endif
(void)data;
return CURLE_OK;
}
CURLcode Curl_auth_gsasl_token(struct Curl_easy *data,
const struct bufref *chlg,
struct gsasldata *gsasl,
struct bufref *out)
{
int res;
char *response;
size_t outlen;
res = gsasl_step(gsasl->client,
(const char *) Curl_bufref_ptr(chlg), Curl_bufref_len(chlg),
&response, &outlen);
if(res != GSASL_OK && res != GSASL_NEEDS_MORE) {
failf(data, "GSASL step: %s\n", gsasl_strerror(res));
return CURLE_BAD_CONTENT_ENCODING;
}
Curl_bufref_set(out, response, outlen, gsasl_free);
return CURLE_OK;
}
void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl)
{
gsasl_finish(gsasl->client);
gsasl->client = NULL;
gsasl_done(gsasl->ctx);
gsasl->ctx = NULL;
}
#endif

323
deps/curl/lib/vauth/krb5_gssapi.c vendored Normal file
View File

@ -0,0 +1,323 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "curl_sasl.h"
#include "urldata.h"
#include "curl_gssapi.h"
#include "sendf.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_gssapi_supported()
*
* This is used to evaluate if GSSAPI (Kerberos V5) is supported.
*
* Parameters: None
*
* Returns TRUE if Kerberos V5 is supported by the GSS-API library.
*/
bool Curl_auth_is_gssapi_supported(void)
{
return TRUE;
}
/*
* Curl_auth_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.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in[ - The host name.
* mutual_auth [in] - Flag specifying whether or not mutual authentication
* is enabled.
* chlg [in] - Optional challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual_auth,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out)
{
CURLcode result = CURLE_OK;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
(void) userp;
(void) passwdp;
if(!krb5->spn) {
/* Generate our SPN */
char *spn = Curl_auth_build_spn(service, NULL, host);
if(!spn)
return CURLE_OUT_OF_MEMORY;
/* Populate the SPN structure */
spn_token.value = spn;
spn_token.length = strlen(spn);
/* Import the SPN */
major_status = gss_import_name(&minor_status, &spn_token,
GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_import_name() failed: ",
major_status, minor_status);
free(spn);
return CURLE_AUTH_ERROR;
}
free(spn);
}
if(chlg) {
if(!Curl_bufref_len(chlg)) {
infof(data, "GSSAPI handshake failure (empty challenge message)");
return CURLE_BAD_CONTENT_ENCODING;
}
input_token.value = (void *) Curl_bufref_ptr(chlg);
input_token.length = Curl_bufref_len(chlg);
}
major_status = Curl_gss_init_sec_context(data,
&minor_status,
&krb5->context,
krb5->spn,
&Curl_krb5_mech_oid,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
&output_token,
mutual_auth,
NULL);
if(GSS_ERROR(major_status)) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
return CURLE_AUTH_ERROR;
}
if(output_token.value && output_token.length) {
result = Curl_bufref_memdup(out, output_token.value, output_token.length);
gss_release_buffer(&unused_status, &output_token);
}
else
Curl_bufref_set(out, mutual_auth? "": NULL, 0, NULL);
return result;
}
/*
* Curl_auth_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.
* authzid [in] - The authorization identity if some.
* chlg [in] - Optional challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *authzid,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out)
{
CURLcode result = CURLE_OK;
size_t messagelen = 0;
unsigned char *message = NULL;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
unsigned char *indata;
gss_qop_t qop = GSS_C_QOP_DEFAULT;
unsigned int sec_layer = 0;
unsigned int max_size = 0;
/* Ensure we have a valid challenge message */
if(!Curl_bufref_len(chlg)) {
infof(data, "GSSAPI handshake failure (empty security message)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Setup the challenge "input" security buffer */
input_token.value = (void *) Curl_bufref_ptr(chlg);
input_token.length = Curl_bufref_len(chlg);
/* Decrypt the inbound challenge and obtain the qop */
major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
&output_token, NULL, &qop);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_unwrap() failed: ",
major_status, minor_status);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Not 4 octets long so fail as per RFC4752 Section 3.1 */
if(output_token.length != 4) {
infof(data, "GSSAPI handshake failure (invalid security data)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Extract the security layer and the maximum message size */
indata = output_token.value;
sec_layer = indata[0];
max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
/* Free the challenge as it is not required anymore */
gss_release_buffer(&unused_status, &output_token);
/* Process the security layer */
if(!(sec_layer & GSSAUTH_P_NONE)) {
infof(data, "GSSAPI handshake failure (invalid security layer)");
return CURLE_BAD_CONTENT_ENCODING;
}
sec_layer &= GSSAUTH_P_NONE; /* We do not support a security layer */
/* Process the maximum message size the server can receive */
if(max_size > 0) {
/* The server has told us it supports a maximum receive buffer, however, as
we don't require one unless we are encrypting data, we tell the server
our receive buffer is zero. */
max_size = 0;
}
/* Allocate our message */
messagelen = 4;
if(authzid)
messagelen += strlen(authzid);
message = malloc(messagelen);
if(!message)
return CURLE_OUT_OF_MEMORY;
/* Populate the message with the security layer and client supported receive
message size. */
message[0] = sec_layer & 0xFF;
message[1] = (max_size >> 16) & 0xFF;
message[2] = (max_size >> 8) & 0xFF;
message[3] = max_size & 0xFF;
/* If given, append the authorization identity. */
if(authzid && *authzid)
memcpy(message + 4, authzid, messagelen - 4);
/* Setup the "authentication data" security buffer */
input_token.value = message;
input_token.length = messagelen;
/* Encrypt the data */
major_status = gss_wrap(&minor_status, krb5->context, 0,
GSS_C_QOP_DEFAULT, &input_token, NULL,
&output_token);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_wrap() failed: ",
major_status, minor_status);
free(message);
return CURLE_AUTH_ERROR;
}
/* Return the response. */
result = Curl_bufref_memdup(out, output_token.value, output_token.length);
/* Free the output buffer */
gss_release_buffer(&unused_status, &output_token);
/* Free the message buffer */
free(message);
return result;
}
/*
* Curl_auth_cleanup_gssapi()
*
* This is used to clean up the GSSAPI (Kerberos V5) specific data.
*
* Parameters:
*
* krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
*
*/
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
OM_uint32 minor_status;
/* Free our security context */
if(krb5->context != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER);
krb5->context = GSS_C_NO_CONTEXT;
}
/* Free the SPN */
if(krb5->spn != GSS_C_NO_NAME) {
gss_release_name(&minor_status, &krb5->spn);
krb5->spn = GSS_C_NO_NAME;
}
}
#endif /* HAVE_GSSAPI && USE_KERBEROS5 */

474
deps/curl/lib/vauth/krb5_sspi.c vendored Normal file
View File

@ -0,0 +1,474 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_gssapi_supported()
*
* This is used to evaluate if GSSAPI (Kerberos V5) is supported.
*
* Parameters: None
*
* Returns TRUE if Kerberos V5 is supported by Windows SSPI.
*/
bool Curl_auth_is_gssapi_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for Kerberos */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_KERBEROS),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_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 in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* mutual_auth [in] - Flag specifying whether or not mutual authentication
* is enabled.
* chlg [in] - Optional challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual_auth,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out)
{
CURLcode result = CURLE_OK;
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->spn) {
/* Generate our SPN */
krb5->spn = Curl_auth_build_spn(service, host, NULL);
if(!krb5->spn)
return CURLE_OUT_OF_MEMORY;
}
if(!krb5->output_token) {
/* Query the security package for Kerberos */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_KERBEROS),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
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;
}
if(!krb5->credentials) {
/* Do we have credentials to use or are we using single sign-on? */
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 = calloc(1, sizeof(CredHandle));
if(!krb5->credentials)
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
status = s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)
TEXT(SP_NAME_KERBEROS),
SECPKG_CRED_OUTBOUND, NULL,
krb5->p_identity, NULL, NULL,
krb5->credentials, &expiry);
if(status != SEC_E_OK)
return CURLE_LOGIN_DENIED;
/* Allocate our new context handle */
krb5->context = calloc(1, sizeof(CtxtHandle));
if(!krb5->context)
return CURLE_OUT_OF_MEMORY;
}
if(chlg) {
if(!Curl_bufref_len(chlg)) {
infof(data, "GSSAPI handshake failure (empty challenge message)");
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 = (void *) Curl_bufref_ptr(chlg);
chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
}
/* 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,
(mutual_auth ?
ISC_REQ_MUTUAL_AUTH : 0),
0, SECURITY_NATIVE_DREP,
chlg ? &chlg_desc : NULL, 0,
&context,
&resp_desc, &attrs,
&expiry);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
return CURLE_AUTH_ERROR;
if(memcmp(&context, krb5->context, sizeof(context))) {
s_pSecFn->DeleteSecurityContext(krb5->context);
memcpy(krb5->context, &context, sizeof(context));
}
if(resp_buf.cbBuffer) {
result = Curl_bufref_memdup(out, resp_buf.pvBuffer, resp_buf.cbBuffer);
}
else if(mutual_auth)
Curl_bufref_set(out, "", 0, NULL);
else
Curl_bufref_set(out, NULL, 0, NULL);
return result;
}
/*
* Curl_auth_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.
* authzid [in] - The authorization identity if some.
* chlg [in] - The optional challenge message.
* krb5 [in/out] - The Kerberos 5 data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *authzid,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out)
{
size_t offset = 0;
size_t messagelen = 0;
size_t appdatalen = 0;
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 char *indata;
unsigned long qop = 0;
unsigned long sec_layer = 0;
unsigned long max_size = 0;
SecPkgContext_Sizes sizes;
SECURITY_STATUS status;
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
/* Ensure we have a valid challenge message */
if(!Curl_bufref_len(chlg)) {
infof(data, "GSSAPI handshake failure (empty security message)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Get our response size information */
status = s_pSecFn->QueryContextAttributes(krb5->context,
SECPKG_ATTR_SIZES,
&sizes);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
if(status != SEC_E_OK)
return CURLE_AUTH_ERROR;
/* Setup the "input" security buffer */
input_desc.ulVersion = SECBUFFER_VERSION;
input_desc.cBuffers = 2;
input_desc.pBuffers = input_buf;
input_buf[0].BufferType = SECBUFFER_STREAM;
input_buf[0].pvBuffer = (void *) Curl_bufref_ptr(chlg);
input_buf[0].cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
input_buf[1].BufferType = SECBUFFER_DATA;
input_buf[1].pvBuffer = NULL;
input_buf[1].cbBuffer = 0;
/* Decrypt the inbound challenge and obtain the qop */
status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
if(status != SEC_E_OK) {
infof(data, "GSSAPI handshake failure (empty security message)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Not 4 octets long so fail as per RFC4752 Section 3.1 */
if(input_buf[1].cbBuffer != 4) {
infof(data, "GSSAPI handshake failure (invalid security data)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Extract the security layer and the maximum message size */
indata = input_buf[1].pvBuffer;
sec_layer = indata[0];
max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
/* Free the challenge as it is not required anymore */
s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
/* Process the security layer */
if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
infof(data, "GSSAPI handshake failure (invalid security layer)");
return CURLE_BAD_CONTENT_ENCODING;
}
sec_layer &= KERB_WRAP_NO_ENCRYPT; /* We do not support a security layer */
/* Process the maximum message size the server can receive */
if(max_size > 0) {
/* The server has told us it supports a maximum receive buffer, however, as
we don't require one unless we are encrypting data, we tell the server
our receive buffer is zero. */
max_size = 0;
}
/* Allocate the trailer */
trailer = malloc(sizes.cbSecurityTrailer);
if(!trailer)
return CURLE_OUT_OF_MEMORY;
/* Allocate our message */
messagelen = 4;
if(authzid)
messagelen += strlen(authzid);
message = malloc(messagelen);
if(!message) {
free(trailer);
return CURLE_OUT_OF_MEMORY;
}
/* Populate the message with the security layer and client supported receive
message size. */
message[0] = sec_layer & 0xFF;
message[1] = (max_size >> 16) & 0xFF;
message[2] = (max_size >> 8) & 0xFF;
message[3] = max_size & 0xFF;
/* If given, append the authorization identity. */
if(authzid && *authzid)
memcpy(message + 4, authzid, messagelen - 4);
/* Allocate the padding */
padding = malloc(sizes.cbBlockSize);
if(!padding) {
free(message);
free(trailer);
return CURLE_OUT_OF_MEMORY;
}
/* Setup the "authentication data" security buffer */
wrap_desc.ulVersion = SECBUFFER_VERSION;
wrap_desc.cBuffers = 3;
wrap_desc.pBuffers = wrap_buf;
wrap_buf[0].BufferType = SECBUFFER_TOKEN;
wrap_buf[0].pvBuffer = trailer;
wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
wrap_buf[1].BufferType = SECBUFFER_DATA;
wrap_buf[1].pvBuffer = message;
wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
wrap_buf[2].BufferType = SECBUFFER_PADDING;
wrap_buf[2].pvBuffer = padding;
wrap_buf[2].cbBuffer = sizes.cbBlockSize;
/* Encrypt the data */
status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
&wrap_desc, 0);
if(status != SEC_E_OK) {
free(padding);
free(message);
free(trailer);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
/* Allocate the encryption (wrap) buffer */
appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
wrap_buf[2].cbBuffer;
appdata = malloc(appdatalen);
if(!appdata) {
free(padding);
free(message);
free(trailer);
return CURLE_OUT_OF_MEMORY;
}
/* Populate the encryption buffer */
memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
offset += wrap_buf[0].cbBuffer;
memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
offset += wrap_buf[1].cbBuffer;
memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
/* Free all of our local buffers */
free(padding);
free(message);
free(trailer);
/* Return the response. */
Curl_bufref_set(out, appdata, appdatalen, curl_free);
return CURLE_OK;
}
/*
* Curl_auth_cleanup_gssapi()
*
* This is used to clean up the GSSAPI (Kerberos V5) specific data.
*
* Parameters:
*
* krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
*
*/
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
/* Free our security context */
if(krb5->context) {
s_pSecFn->DeleteSecurityContext(krb5->context);
free(krb5->context);
krb5->context = NULL;
}
/* Free our credentials handle */
if(krb5->credentials) {
s_pSecFn->FreeCredentialsHandle(krb5->credentials);
free(krb5->credentials);
krb5->credentials = NULL;
}
/* Free our identity */
Curl_sspi_free_identity(krb5->p_identity);
krb5->p_identity = NULL;
/* Free the SPN and output token */
Curl_safefree(krb5->spn);
Curl_safefree(krb5->output_token);
/* Reset any variables */
krb5->token_max = 0;
}
#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5 */

780
deps/curl/lib/vauth/ntlm.c vendored Normal file
View File

@ -0,0 +1,780 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
/*
* NTLM details:
*
* https://davenport.sourceforge.net/ntlm.html
* https://www.innovation.ch/java/ntlm.html
*/
#define DEBUG_ME 0
#include "urldata.h"
#include "sendf.h"
#include "curl_ntlm_core.h"
#include "curl_gethostname.h"
#include "curl_multibyte.h"
#include "curl_md5.h"
#include "warnless.h"
#include "rand.h"
#include "vtls/vtls.h"
#define BUILDING_CURL_NTLM_MSGS_C
#include "vauth/vauth.h"
#include "vauth/ntlm.h"
#include "curl_endian.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
/* The fixed host name we provide, in order to not leak our real local host
name. Copy the name used by Firefox. */
#define NTLM_HOSTNAME "WORKSTATION"
#if DEBUG_ME
# define DEBUG_OUT(x) x
static void ntlm_print_flags(FILE *handle, unsigned long flags)
{
if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
if(flags & NTLMFLAG_NEGOTIATE_OEM)
fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
if(flags & NTLMFLAG_REQUEST_TARGET)
fprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
if(flags & (1<<3))
fprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
if(flags & NTLMFLAG_NEGOTIATE_SIGN)
fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
if(flags & NTLMFLAG_NEGOTIATE_SEAL)
fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
if(flags & (1<<10))
fprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS)
fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS ");
if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
if(flags & (1<<24))
fprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
if(flags & (1<<25))
fprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
if(flags & (1<<26))
fprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
if(flags & (1<<27))
fprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
if(flags & (1<<28))
fprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
if(flags & NTLMFLAG_NEGOTIATE_128)
fprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
if(flags & NTLMFLAG_NEGOTIATE_56)
fprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
}
static void ntlm_print_hex(FILE *handle, const char *buf, size_t len)
{
const char *p = buf;
(void) handle;
fprintf(stderr, "0x");
while(len-- > 0)
fprintf(stderr, "%02.2x", (unsigned int)*p++);
}
#else
# define DEBUG_OUT(x) Curl_nop_stmt
#endif
/*
* ntlm_decode_type2_target()
*
* This is used to decode the "target info" in the NTLM type-2 message
* received.
*
* Parameters:
*
* data [in] - The session handle.
* type2ref [in] - The type-2 message.
* ntlm [in/out] - The NTLM data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
const struct bufref *type2ref,
struct ntlmdata *ntlm)
{
unsigned short target_info_len = 0;
unsigned int target_info_offset = 0;
const unsigned char *type2 = Curl_bufref_ptr(type2ref);
size_t type2len = Curl_bufref_len(type2ref);
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
if(type2len >= 48) {
target_info_len = Curl_read16_le(&type2[40]);
target_info_offset = Curl_read32_le(&type2[44]);
if(target_info_len > 0) {
if((target_info_offset > type2len) ||
(target_info_offset + target_info_len) > type2len ||
target_info_offset < 48) {
infof(data, "NTLM handshake failure (bad type-2 message). "
"Target Info Offset Len is set incorrect by the peer");
return CURLE_BAD_CONTENT_ENCODING;
}
free(ntlm->target_info); /* replace any previous data */
ntlm->target_info = malloc(target_info_len);
if(!ntlm->target_info)
return CURLE_OUT_OF_MEMORY;
memcpy(ntlm->target_info, &type2[target_info_offset], target_info_len);
}
}
ntlm->target_info_len = target_info_len;
return CURLE_OK;
}
/*
NTLM message structure notes:
A 'short' is a 'network short', a little-endian 16-bit unsigned value.
A 'long' is a 'network long', a little-endian, 32-bit unsigned value.
A 'security buffer' represents a triplet used to point to a buffer,
consisting of two shorts and one long:
1. A 'short' containing the length of the buffer content in bytes.
2. A 'short' containing the allocated space for the buffer in bytes.
3. A 'long' containing the offset to the start of the buffer in bytes,
from the beginning of the NTLM message.
*/
/*
* Curl_auth_is_ntlm_supported()
*
* This is used to evaluate if NTLM is supported.
*
* Parameters: None
*
* Returns TRUE as NTLM as handled by libcurl.
*/
bool Curl_auth_is_ntlm_supported(void)
{
return TRUE;
}
/*
* Curl_auth_decode_ntlm_type2_message()
*
* This is used to decode an NTLM type-2 message. The raw NTLM message is
* checked * for validity before the appropriate data for creating a type-3
* message is * written to the given NTLM data structure.
*
* Parameters:
*
* data [in] - The session handle.
* type2ref [in] - The type-2 message.
* ntlm [in/out] - The NTLM data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const struct bufref *type2ref,
struct ntlmdata *ntlm)
{
static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
/* NTLM type-2 message structure:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x02000000)
12 Target Name security buffer
20 Flags long
24 Challenge 8 bytes
(32) Context 8 bytes (two consecutive longs) (*)
(40) Target Information security buffer (*)
(48) OS Version Structure 8 bytes (*)
32 (48) (56) Start of data block (*)
(*) -> Optional
*/
CURLcode result = CURLE_OK;
const unsigned char *type2 = Curl_bufref_ptr(type2ref);
size_t type2len = Curl_bufref_len(type2ref);
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void)data;
#endif
ntlm->flags = 0;
if((type2len < 32) ||
(memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) ||
(memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) {
/* This was not a good enough type-2 message */
infof(data, "NTLM handshake failure (bad type-2 message)");
return CURLE_BAD_CONTENT_ENCODING;
}
ntlm->flags = Curl_read32_le(&type2[20]);
memcpy(ntlm->nonce, &type2[24], 8);
if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) {
result = ntlm_decode_type2_target(data, type2ref, ntlm);
if(result) {
infof(data, "NTLM handshake failure (bad type-2 message)");
return result;
}
}
DEBUG_OUT({
fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
ntlm_print_flags(stderr, ntlm->flags);
fprintf(stderr, "\n nonce=");
ntlm_print_hex(stderr, (char *)ntlm->nonce, 8);
fprintf(stderr, "\n****\n");
fprintf(stderr, "**** Header %s\n ", header);
});
return result;
}
/* copy the source to the destination and fill in zeroes in every
other destination byte! */
static void unicodecpy(unsigned char *dest, const char *src, size_t length)
{
size_t i;
for(i = 0; i < length; i++) {
dest[2 * i] = (unsigned char)src[i];
dest[2 * i + 1] = '\0';
}
}
/*
* Curl_auth_create_ntlm_type1_message()
*
* This is used to generate an NTLM type-1 message ready for sending to the
* recipient using the appropriate compile time crypto API.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* ntlm [in/out] - The NTLM data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *hostname,
struct ntlmdata *ntlm,
struct bufref *out)
{
/* NTLM type-1 message structure:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x01000000)
12 Flags long
(16) Supplied Domain security buffer (*)
(24) Supplied Workstation security buffer (*)
(32) OS Version Structure 8 bytes (*)
(32) (40) Start of data block (*)
(*) -> Optional
*/
size_t size;
char *ntlmbuf;
const char *host = ""; /* empty */
const char *domain = ""; /* empty */
size_t hostlen = 0;
size_t domlen = 0;
size_t hostoff = 0;
size_t domoff = hostoff + hostlen; /* This is 0: remember that host and
domain are empty */
(void)data;
(void)userp;
(void)passwdp;
(void)service;
(void)hostname;
/* Clean up any former leftovers and initialise to defaults */
Curl_auth_cleanup_ntlm(ntlm);
ntlmbuf = aprintf(NTLMSSP_SIGNATURE "%c"
"\x01%c%c%c" /* 32-bit type = 1 */
"%c%c%c%c" /* 32-bit NTLM flag field */
"%c%c" /* domain length */
"%c%c" /* domain allocated space */
"%c%c" /* domain name offset */
"%c%c" /* 2 zeroes */
"%c%c" /* host length */
"%c%c" /* host allocated space */
"%c%c" /* host name offset */
"%c%c" /* 2 zeroes */
"%s" /* host name */
"%s", /* domain string */
0, /* trailing zero */
0, 0, 0, /* part of type-1 long */
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0, 0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0, 0,
host, /* this is empty */
domain /* this is empty */);
if(!ntlmbuf)
return CURLE_OUT_OF_MEMORY;
/* Initial packet length */
size = 32 + hostlen + domlen;
DEBUG_OUT({
fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x "
"0x%08.8x ",
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
ntlm_print_flags(stderr,
NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
fprintf(stderr, "\n****\n");
});
Curl_bufref_set(out, ntlmbuf, size, curl_free);
return CURLE_OK;
}
/*
* Curl_auth_create_ntlm_type3_message()
*
* This is used to generate an already encoded NTLM type-3 message ready for
* sending to the recipient using the appropriate compile time crypto API.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* ntlm [in/out] - The NTLM data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
struct bufref *out)
{
/* NTLM type-3 message structure:
Index Description Content
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
(0x4e544c4d53535000)
8 NTLM Message Type long (0x03000000)
12 LM/LMv2 Response security buffer
20 NTLM/NTLMv2 Response security buffer
28 Target Name security buffer
36 User Name security buffer
44 Workstation Name security buffer
(52) Session Key security buffer (*)
(60) Flags long (*)
(64) OS Version Structure 8 bytes (*)
52 (64) (72) Start of data block
(*) -> Optional
*/
CURLcode result = CURLE_OK;
size_t size;
unsigned char ntlmbuf[NTLM_BUFSIZE];
int lmrespoff;
unsigned char lmresp[24]; /* fixed-size */
int ntrespoff;
unsigned int ntresplen = 24;
unsigned char ntresp[24]; /* fixed-size */
unsigned char *ptr_ntresp = &ntresp[0];
unsigned char *ntlmv2resp = NULL;
bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE;
char host[HOSTNAME_MAX + 1] = "";
const char *user;
const char *domain = "";
size_t hostoff = 0;
size_t useroff = 0;
size_t domoff = 0;
size_t hostlen = 0;
size_t userlen = 0;
size_t domlen = 0;
memset(lmresp, 0, sizeof(lmresp));
memset(ntresp, 0, sizeof(ntresp));
user = strchr(userp, '\\');
if(!user)
user = strchr(userp, '/');
if(user) {
domain = userp;
domlen = (user - domain);
user++;
}
else
user = userp;
userlen = strlen(user);
#ifndef NTLM_HOSTNAME
/* Get the machine's un-qualified host name as NTLM doesn't like the fully
qualified domain name */
if(Curl_gethostname(host, sizeof(host))) {
infof(data, "gethostname() failed, continuing without");
hostlen = 0;
}
else {
hostlen = strlen(host);
}
#else
(void)msnprintf(host, sizeof(host), "%s", NTLM_HOSTNAME);
hostlen = sizeof(NTLM_HOSTNAME)-1;
#endif
if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
unsigned char ntbuffer[0x18];
unsigned char entropy[8];
unsigned char ntlmv2hash[0x18];
/* Full NTLM version 2
Although this cannot be negotiated, it is used here if available, as
servers featuring extended security are likely supporting also
NTLMv2. */
result = Curl_rand(data, entropy, 8);
if(result)
return result;
result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer);
if(result)
return result;
result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen,
ntbuffer, ntlmv2hash);
if(result)
return result;
/* LMv2 response */
result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy,
&ntlm->nonce[0], lmresp);
if(result)
return result;
/* NTLMv2 response */
result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy,
ntlm, &ntlmv2resp, &ntresplen);
if(result)
return result;
ptr_ntresp = ntlmv2resp;
}
else {
unsigned char ntbuffer[0x18];
unsigned char lmbuffer[0x18];
/* NTLM version 1 */
result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
result = Curl_ntlm_core_mk_lm_hash(passwdp, lmbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
ntlm->flags &= ~NTLMFLAG_NEGOTIATE_NTLM2_KEY;
/* A safer but less compatible alternative is:
* Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
* See https://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
}
if(unicode) {
domlen = domlen * 2;
userlen = userlen * 2;
hostlen = hostlen * 2;
}
lmrespoff = 64; /* size of the message header */
ntrespoff = lmrespoff + 0x18;
domoff = ntrespoff + ntresplen;
useroff = domoff + domlen;
hostoff = useroff + userlen;
/* Create the big type-3 message binary blob */
size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
NTLMSSP_SIGNATURE "%c"
"\x03%c%c%c" /* 32-bit type = 3 */
"%c%c" /* LanManager length */
"%c%c" /* LanManager allocated space */
"%c%c" /* LanManager offset */
"%c%c" /* 2 zeroes */
"%c%c" /* NT-response length */
"%c%c" /* NT-response allocated space */
"%c%c" /* NT-response offset */
"%c%c" /* 2 zeroes */
"%c%c" /* domain length */
"%c%c" /* domain allocated space */
"%c%c" /* domain name offset */
"%c%c" /* 2 zeroes */
"%c%c" /* user length */
"%c%c" /* user allocated space */
"%c%c" /* user offset */
"%c%c" /* 2 zeroes */
"%c%c" /* host length */
"%c%c" /* host allocated space */
"%c%c" /* host offset */
"%c%c" /* 2 zeroes */
"%c%c" /* session key length (unknown purpose) */
"%c%c" /* session key allocated space (unknown purpose) */
"%c%c" /* session key offset (unknown purpose) */
"%c%c" /* 2 zeroes */
"%c%c%c%c", /* flags */
/* domain string */
/* user string */
/* host string */
/* LanManager response */
/* NT response */
0, /* null-termination */
0, 0, 0, /* type-3 long, the 24 upper bits */
SHORTPAIR(0x18), /* LanManager response length, twice */
SHORTPAIR(0x18),
SHORTPAIR(lmrespoff),
0x0, 0x0,
SHORTPAIR(ntresplen), /* NT-response length, twice */
SHORTPAIR(ntresplen),
SHORTPAIR(ntrespoff),
0x0, 0x0,
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0x0, 0x0,
SHORTPAIR(userlen),
SHORTPAIR(userlen),
SHORTPAIR(useroff),
0x0, 0x0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
LONGQUARTET(ntlm->flags));
DEBUGASSERT(size == 64);
DEBUGASSERT(size == (size_t)lmrespoff);
/* We append the binary hashes */
if(size < (NTLM_BUFSIZE - 0x18)) {
memcpy(&ntlmbuf[size], lmresp, 0x18);
size += 0x18;
}
DEBUG_OUT({
fprintf(stderr, "**** TYPE3 header lmresp=");
ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
});
/* ntresplen + size should not be risking an integer overflow here */
if(ntresplen + size > sizeof(ntlmbuf)) {
failf(data, "incoming NTLM message too big");
return CURLE_OUT_OF_MEMORY;
}
DEBUGASSERT(size == (size_t)ntrespoff);
memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
size += ntresplen;
DEBUG_OUT({
fprintf(stderr, "\n ntresp=");
ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen);
});
free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */
DEBUG_OUT({
fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
LONGQUARTET(ntlm->flags), ntlm->flags);
ntlm_print_flags(stderr, ntlm->flags);
fprintf(stderr, "\n****\n");
});
/* Make sure that the domain, user and host strings fit in the
buffer before we copy them there. */
if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) {
failf(data, "user + domain + host name too big");
return CURLE_OUT_OF_MEMORY;
}
DEBUGASSERT(size == domoff);
if(unicode)
unicodecpy(&ntlmbuf[size], domain, domlen / 2);
else
memcpy(&ntlmbuf[size], domain, domlen);
size += domlen;
DEBUGASSERT(size == useroff);
if(unicode)
unicodecpy(&ntlmbuf[size], user, userlen / 2);
else
memcpy(&ntlmbuf[size], user, userlen);
size += userlen;
DEBUGASSERT(size == hostoff);
if(unicode)
unicodecpy(&ntlmbuf[size], host, hostlen / 2);
else
memcpy(&ntlmbuf[size], host, hostlen);
size += hostlen;
/* Return the binary blob. */
result = Curl_bufref_memdup(out, ntlmbuf, size);
Curl_auth_cleanup_ntlm(ntlm);
return result;
}
/*
* Curl_auth_cleanup_ntlm()
*
* This is used to clean up the NTLM specific data.
*
* Parameters:
*
* ntlm [in/out] - The NTLM data struct being cleaned up.
*
*/
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
/* Free the target info */
Curl_safefree(ntlm->target_info);
/* Reset any variables */
ntlm->target_info_len = 0;
}
#endif /* USE_NTLM && !USE_WINDOWS_SSPI */

143
deps/curl/lib/vauth/ntlm.h vendored Normal file
View File

@ -0,0 +1,143 @@
#ifndef HEADER_VAUTH_NTLM_H
#define HEADER_VAUTH_NTLM_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_NTLM
/* NTLM buffer fixed size, large enough for long user + host + domain */
#define NTLM_BUFSIZE 1024
/* Stuff only required for curl_ntlm_msgs.c */
#ifdef BUILDING_CURL_NTLM_MSGS_C
/* Flag bits definitions based on
https://davenport.sourceforge.net/ntlm.html */
#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
/* Indicates that Unicode strings are supported for use in security buffer
data. */
#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
/* Indicates that OEM strings are supported for use in security buffer data. */
#define NTLMFLAG_REQUEST_TARGET (1<<2)
/* Requests that the server's authentication realm be included in the Type 2
message. */
/* unknown (1<<3) */
#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
/* Specifies that authenticated communication between the client and server
should carry a digital signature (message integrity). */
#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
/* Specifies that authenticated communication between the client and server
should be encrypted (message confidentiality). */
#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
/* Indicates that datagram authentication is being used. */
#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
/* Indicates that the LAN Manager session key should be used for signing and
sealing authenticated communications. */
#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
/* Indicates that NTLM authentication is being used. */
/* unknown (1<<10) */
#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11)
/* Sent by the client in the Type 3 message to indicate that an anonymous
context has been established. This also affects the response fields. */
#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
/* Sent by the client in the Type 1 message to indicate that a desired
authentication realm is included in the message. */
#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
/* Sent by the client in the Type 1 message to indicate that the client
workstation's name is included in the message. */
#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
/* Sent by the server to indicate that the server and client are on the same
machine. Implies that the client may use a pre-established local security
context rather than responding to the challenge. */
#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
/* Indicates that authenticated communication between the client and server
should be signed with a "dummy" signature. */
#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a domain. */
#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a server. */
#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a share. Presumably, this is for share-level
authentication. Usage is unclear. */
#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
/* Indicates that the NTLM2 signing and sealing scheme should be used for
protecting authenticated communications. */
#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
/* unknown purpose */
#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
/* unknown purpose */
#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
/* unknown purpose */
#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
/* Sent by the server in the Type 2 message to indicate that it is including a
Target Information block in the message. */
/* unknown (1<24) */
/* unknown (1<25) */
/* unknown (1<26) */
/* unknown (1<27) */
/* unknown (1<28) */
#define NTLMFLAG_NEGOTIATE_128 (1<<29)
/* Indicates that 128-bit encryption is supported. */
#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
/* Indicates that the client will provide an encrypted master key in
the "Session Key" field of the Type 3 message. */
#define NTLMFLAG_NEGOTIATE_56 (1<<31)
/* Indicates that 56-bit encryption is supported. */
#endif /* BUILDING_CURL_NTLM_MSGS_C */
#endif /* USE_NTLM */
#endif /* HEADER_VAUTH_NTLM_H */

372
deps/curl/lib/vauth/ntlm_sspi.c vendored Normal file
View File

@ -0,0 +1,372 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_ntlm_core.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_ntlm_supported()
*
* This is used to evaluate if NTLM is supported.
*
* Parameters: None
*
* Returns TRUE if NTLM is supported by Windows SSPI.
*/
bool Curl_auth_is_ntlm_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for NTLM */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_create_ntlm_type1_message()
*
* This is used to generate an already encoded NTLM type-1 message ready for
* sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* ntlm [in/out] - The NTLM data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
struct ntlmdata *ntlm,
struct bufref *out)
{
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_auth_cleanup_ntlm(ntlm);
/* Query the security package for NTLM */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
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 = calloc(1, sizeof(CredHandle));
if(!ntlm->credentials)
return CURLE_OUT_OF_MEMORY;
/* 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 = calloc(1, sizeof(CtxtHandle));
if(!ntlm->context)
return CURLE_OUT_OF_MEMORY;
ntlm->spn = Curl_auth_build_spn(service, host, NULL);
if(!ntlm->spn)
return CURLE_OUT_OF_MEMORY;
/* 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,
ntlm->spn,
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_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
return CURLE_AUTH_ERROR;
/* Return the response. */
Curl_bufref_set(out, ntlm->output_token, type_1_buf.cbBuffer, NULL);
return CURLE_OK;
}
/*
* Curl_auth_decode_ntlm_type2_message()
*
* This is used to decode an already encoded NTLM type-2 message.
*
* Parameters:
*
* data [in] - The session handle.
* type2 [in] - The type-2 message.
* ntlm [in/out] - The NTLM data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const struct bufref *type2,
struct ntlmdata *ntlm)
{
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
/* Ensure we have a valid type-2 message */
if(!Curl_bufref_len(type2)) {
infof(data, "NTLM handshake failure (empty type-2 message)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Store the challenge for later use */
ntlm->input_token = malloc(Curl_bufref_len(type2) + 1);
if(!ntlm->input_token)
return CURLE_OUT_OF_MEMORY;
memcpy(ntlm->input_token, Curl_bufref_ptr(type2), Curl_bufref_len(type2));
ntlm->input_token[Curl_bufref_len(type2)] = '\0';
ntlm->input_token_len = Curl_bufref_len(type2);
return CURLE_OK;
}
/*
* Curl_auth_create_ntlm_type3_message()
* Curl_auth_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] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* ntlm [in/out] - The NTLM data struct being used and modified.
* out [out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
struct bufref *out)
{
CURLcode result = CURLE_OK;
SecBuffer type_2_bufs[2];
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 */
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
(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_bufs[0];
type_2_bufs[0].BufferType = SECBUFFER_TOKEN;
type_2_bufs[0].pvBuffer = ntlm->input_token;
type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len);
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
/* ssl context comes from schannel.
* When extended protection is used in IIS server,
* we have to pass a second SecBuffer to the SecBufferDesc
* otherwise IIS will not pass the authentication (401 response).
* Minimum supported version is Windows 7.
* https://docs.microsoft.com/en-us/security-updates
* /SecurityAdvisories/2009/973811
*/
if(ntlm->sslContext) {
SEC_CHANNEL_BINDINGS channelBindings;
SecPkgContext_Bindings pkgBindings;
pkgBindings.Bindings = &channelBindings;
status = s_pSecFn->QueryContextAttributes(
ntlm->sslContext,
SECPKG_ATTR_ENDPOINT_BINDINGS,
&pkgBindings
);
if(status == SEC_E_OK) {
type_2_desc.cBuffers++;
type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength;
type_2_bufs[1].pvBuffer = pkgBindings.Bindings;
}
}
#endif
/* 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,
ntlm->spn,
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",
status);
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
/* Return the response. */
result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer);
Curl_auth_cleanup_ntlm(ntlm);
return result;
}
/*
* Curl_auth_cleanup_ntlm()
*
* This is used to clean up the NTLM specific data.
*
* Parameters:
*
* ntlm [in/out] - The NTLM data struct being cleaned up.
*
*/
void Curl_auth_cleanup_ntlm(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;
Curl_safefree(ntlm->spn);
}
#endif /* USE_WINDOWS_SSPI && USE_NTLM */

108
deps/curl/lib/vauth/oauth2.c vendored Normal file
View File

@ -0,0 +1,108 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC6749 OAuth 2.0 Authorization Framework
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3) || \
(!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
#include "urldata.h"
#include "vauth/vauth.h"
#include "warnless.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_create_oauth_bearer_message()
*
* This is used to generate an OAuth 2.0 message ready for sending to the
* recipient.
*
* Parameters:
*
* user[in] - The user name.
* host[in] - The host name.
* port[in] - The port(when not Port 80).
* bearer[in] - The bearer token.
* out[out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_oauth_bearer_message(const char *user,
const char *host,
const long port,
const char *bearer,
struct bufref *out)
{
char *oauth;
/* Generate the message */
if(port == 0 || port == 80)
oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host,
bearer);
else
oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user,
host, port, bearer);
if(!oauth)
return CURLE_OUT_OF_MEMORY;
Curl_bufref_set(out, oauth, strlen(oauth), curl_free);
return CURLE_OK;
}
/*
* Curl_auth_create_xoauth_bearer_message()
*
* This is used to generate a XOAuth 2.0 message ready for * sending to the
* recipient.
*
* Parameters:
*
* user[in] - The user name.
* bearer[in] - The bearer token.
* out[out] - The result storage.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_xoauth_bearer_message(const char *user,
const char *bearer,
struct bufref *out)
{
/* Generate the message */
char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
if(!xoauth)
return CURLE_OUT_OF_MEMORY;
Curl_bufref_set(out, xoauth, strlen(xoauth), curl_free);
return CURLE_OK;
}
#endif /* disabled, no users */

281
deps/curl/lib/vauth/spnego_gssapi.c vendored Normal file
View File

@ -0,0 +1,281 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC4178 Simple and Protected GSS-API Negotiation Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_gssapi.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_spnego_supported()
*
* This is used to evaluate if SPNEGO (Negotiate) is supported.
*
* Parameters: None
*
* Returns TRUE if Negotiate supported by the GSS-API library.
*/
bool Curl_auth_is_spnego_supported(void)
{
return TRUE;
}
/*
* Curl_auth_decode_spnego_message()
*
* This is used to decode an already encoded SPNEGO (Negotiate) challenge
* message.
*
* Parameters:
*
* data [in] - The session handle.
* userp [in] - The user name in the format User or Domain\User.
* passwdp [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* chlg64 [in] - The optional base64 encoded challenge message.
* nego [in/out] - The Negotiate data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
const char *user,
const char *password,
const char *service,
const char *host,
const char *chlg64,
struct negotiatedata *nego)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
OM_uint32 major_status;
OM_uint32 minor_status;
OM_uint32 unused_status;
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
(void) user;
(void) password;
if(nego->context && nego->status == GSS_S_COMPLETE) {
/* We finished successfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
Curl_auth_cleanup_spnego(nego);
return CURLE_LOGIN_DENIED;
}
if(!nego->spn) {
/* Generate our SPN */
char *spn = Curl_auth_build_spn(service, NULL, host);
if(!spn)
return CURLE_OUT_OF_MEMORY;
/* Populate the SPN structure */
spn_token.value = spn;
spn_token.length = strlen(spn);
/* Import the SPN */
major_status = gss_import_name(&minor_status, &spn_token,
GSS_C_NT_HOSTBASED_SERVICE,
&nego->spn);
if(GSS_ERROR(major_status)) {
Curl_gss_log_error(data, "gss_import_name() failed: ",
major_status, minor_status);
free(spn);
return CURLE_AUTH_ERROR;
}
free(spn);
}
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "SPNEGO handshake failure (empty challenge message)");
return CURLE_BAD_CONTENT_ENCODING;
}
/* Setup the challenge "input" security buffer */
input_token.value = chlg;
input_token.length = chlglen;
}
/* Generate our challenge-response message */
major_status = Curl_gss_init_sec_context(data,
&minor_status,
&nego->context,
nego->spn,
&Curl_spnego_mech_oid,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
&output_token,
TRUE,
NULL);
/* Free the decoded challenge as it is not required anymore */
Curl_safefree(input_token.value);
nego->status = major_status;
if(GSS_ERROR(major_status)) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
return CURLE_AUTH_ERROR;
}
if(!output_token.value || !output_token.length) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
return CURLE_AUTH_ERROR;
}
/* Free previous token */
if(nego->output_token.length && nego->output_token.value)
gss_release_buffer(&unused_status, &nego->output_token);
nego->output_token = output_token;
return CURLE_OK;
}
/*
* Curl_auth_create_spnego_message()
*
* This is used to generate an already encoded SPNEGO (Negotiate) response
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* nego [in/out] - The Negotiate 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_auth_create_spnego_message(struct negotiatedata *nego,
char **outptr, size_t *outlen)
{
CURLcode result;
OM_uint32 minor_status;
/* Base64 encode the already generated response */
result = Curl_base64_encode(nego->output_token.value,
nego->output_token.length,
outptr, outlen);
if(result) {
gss_release_buffer(&minor_status, &nego->output_token);
nego->output_token.value = NULL;
nego->output_token.length = 0;
return result;
}
if(!*outptr || !*outlen) {
gss_release_buffer(&minor_status, &nego->output_token);
nego->output_token.value = NULL;
nego->output_token.length = 0;
return CURLE_REMOTE_ACCESS_DENIED;
}
return CURLE_OK;
}
/*
* Curl_auth_cleanup_spnego()
*
* This is used to clean up the SPNEGO (Negotiate) specific data.
*
* Parameters:
*
* nego [in/out] - The Negotiate data struct being cleaned up.
*
*/
void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
OM_uint32 minor_status;
/* Free our security context */
if(nego->context != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER);
nego->context = GSS_C_NO_CONTEXT;
}
/* Free the output token */
if(nego->output_token.value) {
gss_release_buffer(&minor_status, &nego->output_token);
nego->output_token.value = NULL;
nego->output_token.length = 0;
}
/* Free the SPN */
if(nego->spn != GSS_C_NO_NAME) {
gss_release_name(&minor_status, &nego->spn);
nego->spn = GSS_C_NO_NAME;
}
/* Reset any variables */
nego->status = 0;
nego->noauthpersist = FALSE;
nego->havenoauthpersist = FALSE;
nego->havenegdata = FALSE;
nego->havemultiplerequests = FALSE;
}
#endif /* HAVE_GSSAPI && USE_SPNEGO */

364
deps/curl/lib/vauth/spnego_sspi.c vendored Normal file
View File

@ -0,0 +1,364 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
* RFC4178 Simple and Protected GSS-API Negotiation Mechanism
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
#include <curl/curl.h>
#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
#include "strerror.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_is_spnego_supported()
*
* This is used to evaluate if SPNEGO (Negotiate) is supported.
*
* Parameters: None
*
* Returns TRUE if Negotiate is supported by Windows SSPI.
*/
bool Curl_auth_is_spnego_supported(void)
{
PSecPkgInfo SecurityPackage;
SECURITY_STATUS status;
/* Query the security package for Negotiate */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_NEGOTIATE),
&SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
s_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
}
/*
* Curl_auth_decode_spnego_message()
*
* This is used to decode an already encoded SPNEGO (Negotiate) challenge
* message.
*
* Parameters:
*
* data [in] - The session handle.
* user [in] - The user name in the format User or Domain\User.
* password [in] - The user's password.
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* chlg64 [in] - The optional base64 encoded challenge message.
* nego [in/out] - The Negotiate data struct being used and modified.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
const char *user,
const char *password,
const char *service,
const char *host,
const char *chlg64,
struct negotiatedata *nego)
{
CURLcode result = CURLE_OK;
size_t chlglen = 0;
unsigned char *chlg = NULL;
PSecPkgInfo SecurityPackage;
SecBuffer chlg_buf[2];
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
unsigned long attrs;
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) data;
#endif
if(nego->context && nego->status == SEC_E_OK) {
/* We finished successfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
Curl_auth_cleanup_spnego(nego);
return CURLE_LOGIN_DENIED;
}
if(!nego->spn) {
/* Generate our SPN */
nego->spn = Curl_auth_build_spn(service, host, NULL);
if(!nego->spn)
return CURLE_OUT_OF_MEMORY;
}
if(!nego->output_token) {
/* Query the security package for Negotiate */
nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_NEGOTIATE),
&SecurityPackage);
if(nego->status != SEC_E_OK) {
failf(data, "SSPI: couldn't get auth info");
return CURLE_AUTH_ERROR;
}
nego->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
s_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our output buffer */
nego->output_token = malloc(nego->token_max);
if(!nego->output_token)
return CURLE_OUT_OF_MEMORY;
}
if(!nego->credentials) {
/* Do we have credentials to use or are we using single sign-on? */
if(user && *user) {
/* Populate our identity structure */
result = Curl_create_sspi_identity(user, password, &nego->identity);
if(result)
return result;
/* Allow proper cleanup of the identity structure */
nego->p_identity = &nego->identity;
}
else
/* Use the current Windows user */
nego->p_identity = NULL;
/* Allocate our credentials handle */
nego->credentials = calloc(1, sizeof(CredHandle));
if(!nego->credentials)
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
nego->status =
s_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)TEXT(SP_NAME_NEGOTIATE),
SECPKG_CRED_OUTBOUND, NULL,
nego->p_identity, NULL, NULL,
nego->credentials, &expiry);
if(nego->status != SEC_E_OK)
return CURLE_AUTH_ERROR;
/* Allocate our new context handle */
nego->context = calloc(1, sizeof(CtxtHandle));
if(!nego->context)
return CURLE_OUT_OF_MEMORY;
}
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
result = Curl_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
/* Ensure we have a valid challenge message */
if(!chlg) {
infof(data, "SPNEGO handshake failure (empty challenge message)");
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[0];
chlg_buf[0].BufferType = SECBUFFER_TOKEN;
chlg_buf[0].pvBuffer = chlg;
chlg_buf[0].cbBuffer = curlx_uztoul(chlglen);
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
/* ssl context comes from Schannel.
* When extended protection is used in IIS server,
* we have to pass a second SecBuffer to the SecBufferDesc
* otherwise IIS will not pass the authentication (401 response).
* Minimum supported version is Windows 7.
* https://docs.microsoft.com/en-us/security-updates
* /SecurityAdvisories/2009/973811
*/
if(nego->sslContext) {
SEC_CHANNEL_BINDINGS channelBindings;
SecPkgContext_Bindings pkgBindings;
pkgBindings.Bindings = &channelBindings;
nego->status = s_pSecFn->QueryContextAttributes(
nego->sslContext,
SECPKG_ATTR_ENDPOINT_BINDINGS,
&pkgBindings
);
if(nego->status == SEC_E_OK) {
chlg_desc.cBuffers++;
chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
chlg_buf[1].cbBuffer = pkgBindings.BindingsLength;
chlg_buf[1].pvBuffer = pkgBindings.Bindings;
}
}
#endif
}
/* 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 = nego->output_token;
resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
/* Generate our challenge-response message */
nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
chlg ? nego->context :
NULL,
nego->spn,
ISC_REQ_CONFIDENTIALITY,
0, SECURITY_NATIVE_DREP,
chlg ? &chlg_desc : NULL,
0, nego->context,
&resp_desc, &attrs,
&expiry);
/* Free the decoded challenge as it is not required anymore */
free(chlg);
if(GSS_ERROR(nego->status)) {
char buffer[STRERROR_LEN];
failf(data, "InitializeSecurityContext failed: %s",
Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
if(nego->status == SEC_I_COMPLETE_NEEDED ||
nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
if(GSS_ERROR(nego->status)) {
char buffer[STRERROR_LEN];
failf(data, "CompleteAuthToken failed: %s",
Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
return CURLE_AUTH_ERROR;
}
}
nego->output_token_length = resp_buf.cbBuffer;
return result;
}
/*
* Curl_auth_create_spnego_message()
*
* This is used to generate an already encoded SPNEGO (Negotiate) response
* message ready for sending to the recipient.
*
* Parameters:
*
* data [in] - The session handle.
* nego [in/out] - The Negotiate 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_auth_create_spnego_message(struct negotiatedata *nego,
char **outptr, size_t *outlen)
{
/* Base64 encode the already generated response */
CURLcode result = Curl_base64_encode((const char *) nego->output_token,
nego->output_token_length, outptr,
outlen);
if(!result && (!*outptr || !*outlen)) {
free(*outptr);
result = CURLE_REMOTE_ACCESS_DENIED;
}
return result;
}
/*
* Curl_auth_cleanup_spnego()
*
* This is used to clean up the SPNEGO (Negotiate) specific data.
*
* Parameters:
*
* nego [in/out] - The Negotiate data struct being cleaned up.
*
*/
void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
/* Free our security context */
if(nego->context) {
s_pSecFn->DeleteSecurityContext(nego->context);
free(nego->context);
nego->context = NULL;
}
/* Free our credentials handle */
if(nego->credentials) {
s_pSecFn->FreeCredentialsHandle(nego->credentials);
free(nego->credentials);
nego->credentials = NULL;
}
/* Free our identity */
Curl_sspi_free_identity(nego->p_identity);
nego->p_identity = NULL;
/* Free the SPN and output token */
Curl_safefree(nego->spn);
Curl_safefree(nego->output_token);
/* Reset any variables */
nego->status = 0;
nego->token_max = 0;
nego->noauthpersist = FALSE;
nego->havenoauthpersist = FALSE;
nego->havenegdata = FALSE;
nego->havemultiplerequests = FALSE;
}
#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */

163
deps/curl/lib/vauth/vauth.c vendored Normal file
View File

@ -0,0 +1,163 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "vauth.h"
#include "urldata.h"
#include "strcase.h"
#include "curl_multibyte.h"
#include "curl_printf.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_auth_build_spn()
*
* This is used to build a SPN string in the following formats:
*
* service/host@realm (Not currently used)
* service/host (Not used by GSS-API)
* service@realm (Not used by Windows SSPI)
*
* Parameters:
*
* service [in] - The service type such as http, smtp, pop or imap.
* host [in] - The host name.
* realm [in] - The realm.
*
* Returns a pointer to the newly allocated SPN.
*/
#if !defined(USE_WINDOWS_SSPI)
char *Curl_auth_build_spn(const char *service, const char *host,
const char *realm)
{
char *spn = NULL;
/* Generate our SPN */
if(host && realm)
spn = aprintf("%s/%s@%s", service, host, realm);
else if(host)
spn = aprintf("%s/%s", service, host);
else if(realm)
spn = aprintf("%s@%s", service, realm);
/* Return our newly allocated SPN */
return spn;
}
#else
TCHAR *Curl_auth_build_spn(const char *service, const char *host,
const char *realm)
{
char *utf8_spn = NULL;
TCHAR *tchar_spn = NULL;
TCHAR *dupe_tchar_spn = NULL;
(void) realm;
/* 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. */
/* Generate our UTF8 based SPN */
utf8_spn = aprintf("%s/%s", service, host);
if(!utf8_spn)
return NULL;
/* Allocate and return a TCHAR based SPN. Since curlx_convert_UTF8_to_tchar
must be freed by curlx_unicodefree we'll dupe the result so that the
pointer this function returns can be normally free'd. */
tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn);
free(utf8_spn);
if(!tchar_spn)
return NULL;
dupe_tchar_spn = _tcsdup(tchar_spn);
curlx_unicodefree(tchar_spn);
return dupe_tchar_spn;
}
#endif /* USE_WINDOWS_SSPI */
/*
* Curl_auth_user_contains_domain()
*
* This is used to test if the specified user contains a Windows domain name as
* follows:
*
* Domain\User (Down-level Logon Name)
* Domain/User (curl Down-level format - for compatibility with existing code)
* User@Domain (User Principal Name)
*
* Note: The user name may be empty when using a GSS-API library or Windows
* SSPI as the user and domain are either obtained from the credentials cache
* when using GSS-API or via the currently logged in user's credentials when
* using Windows SSPI.
*
* Parameters:
*
* user [in] - The user name.
*
* Returns TRUE on success; otherwise FALSE.
*/
bool Curl_auth_user_contains_domain(const char *user)
{
bool valid = FALSE;
if(user && *user) {
/* Check we have a domain name or UPN present */
char *p = strpbrk(user, "\\/@");
valid = (p != NULL && p > user && p < user + strlen(user) - 1 ? TRUE :
FALSE);
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
else
/* User and domain are obtained from the GSS-API credentials cache or the
currently logged in user from Windows */
valid = TRUE;
#endif
return valid;
}
/*
* Curl_auth_ollowed_to_host() tells if authentication, cookies or other
* "sensitive data" can (still) be sent to this host.
*/
bool Curl_auth_allowed_to_host(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
return (!data->state.this_is_a_follow ||
data->set.allow_auth_to_other_hosts ||
(data->state.first_host &&
strcasecompare(data->state.first_host, conn->host.name) &&
(data->state.first_remote_port == conn->remote_port) &&
(data->state.first_remote_protocol == conn->handler->protocol)));
}

238
deps/curl/lib/vauth/vauth.h vendored Normal file
View File

@ -0,0 +1,238 @@
#ifndef HEADER_CURL_VAUTH_H
#define HEADER_CURL_VAUTH_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* 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 https://curl.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include <curl/curl.h>
#include "bufref.h"
struct Curl_easy;
#if !defined(CURL_DISABLE_DIGEST_AUTH)
struct digestdata;
#endif
#if defined(USE_NTLM)
struct ntlmdata;
#endif
#if defined(USE_KERBEROS5)
struct kerberos5data;
#endif
#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO)
struct negotiatedata;
#endif
#if defined(USE_GSASL)
struct gsasldata;
#endif
#if defined(USE_WINDOWS_SSPI)
#define GSS_ERROR(status) ((status) & 0x80000000)
#endif
/*
* Curl_auth_allowed_to_host() tells if authentication, cookies or other
* "sensitive data" can (still) be sent to this host.
*/
bool Curl_auth_allowed_to_host(struct Curl_easy *data);
/* This is used to build a SPN string */
#if !defined(USE_WINDOWS_SSPI)
char *Curl_auth_build_spn(const char *service, const char *host,
const char *realm);
#else
TCHAR *Curl_auth_build_spn(const char *service, const char *host,
const char *realm);
#endif
/* This is used to test if the user contains a Windows domain name */
bool Curl_auth_user_contains_domain(const char *user);
/* This is used to generate a PLAIN cleartext message */
CURLcode Curl_auth_create_plain_message(const char *authzid,
const char *authcid,
const char *passwd,
struct bufref *out);
/* This is used to generate a LOGIN cleartext message */
CURLcode Curl_auth_create_login_message(const char *value,
struct bufref *out);
/* This is used to generate an EXTERNAL cleartext message */
CURLcode Curl_auth_create_external_message(const char *user,
struct bufref *out);
#ifndef CURL_DISABLE_DIGEST_AUTH
/* This is used to generate a CRAM-MD5 response message */
CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg,
const char *userp,
const char *passwdp,
struct bufref *out);
/* This is used to evaluate if DIGEST is supported */
bool Curl_auth_is_digest_supported(void);
/* This is used to generate a base64 encoded DIGEST-MD5 response message */
CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
const struct bufref *chlg,
const char *userp,
const char *passwdp,
const char *service,
struct bufref *out);
/* This is used to decode an HTTP DIGEST challenge message */
CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest);
/* This is used to generate an HTTP DIGEST response message */
CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const unsigned char *request,
const unsigned char *uri,
struct digestdata *digest,
char **outptr, size_t *outlen);
/* This is used to clean up the digest specific data */
void Curl_auth_digest_cleanup(struct digestdata *digest);
#endif /* !CURL_DISABLE_DIGEST_AUTH */
#ifdef USE_GSASL
/* This is used to evaluate if MECH is supported by gsasl */
bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
const char *mech,
struct gsasldata *gsasl);
/* This is used to start a gsasl method */
CURLcode Curl_auth_gsasl_start(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct gsasldata *gsasl);
/* This is used to process and generate a new SASL token */
CURLcode Curl_auth_gsasl_token(struct Curl_easy *data,
const struct bufref *chlg,
struct gsasldata *gsasl,
struct bufref *out);
/* This is used to clean up the gsasl specific data */
void Curl_auth_gsasl_cleanup(struct gsasldata *digest);
#endif
#if defined(USE_NTLM)
/* This is used to evaluate if NTLM is supported */
bool Curl_auth_is_ntlm_supported(void);
/* This is used to generate a base64 encoded NTLM type-1 message */
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
struct ntlmdata *ntlm,
struct bufref *out);
/* This is used to decode a base64 encoded NTLM type-2 message */
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const struct bufref *type2,
struct ntlmdata *ntlm);
/* This is used to generate a base64 encoded NTLM type-3 message */
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
struct bufref *out);
/* This is used to clean up the NTLM specific data */
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm);
#endif /* USE_NTLM */
/* This is used to generate a base64 encoded OAuth 2.0 message */
CURLcode Curl_auth_create_oauth_bearer_message(const char *user,
const char *host,
const long port,
const char *bearer,
struct bufref *out);
/* This is used to generate a base64 encoded XOAuth 2.0 message */
CURLcode Curl_auth_create_xoauth_bearer_message(const char *user,
const char *bearer,
struct bufref *out);
#if defined(USE_KERBEROS5)
/* This is used to evaluate if GSSAPI (Kerberos V5) is supported */
bool Curl_auth_is_gssapi_supported(void);
/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token
message */
CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *host,
const bool mutual,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out);
/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security
token message */
CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
const char *authzid,
const struct bufref *chlg,
struct kerberos5data *krb5,
struct bufref *out);
/* This is used to clean up the GSSAPI specific data */
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5);
#endif /* USE_KERBEROS5 */
#if defined(USE_SPNEGO)
/* This is used to evaluate if SPNEGO (Negotiate) is supported */
bool Curl_auth_is_spnego_supported(void);
/* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge
message */
CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
const char *user,
const char *password,
const char *service,
const char *host,
const char *chlg64,
struct negotiatedata *nego);
/* This is used to generate a base64 encoded SPNEGO (Negotiate) response
message */
CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
char **outptr, size_t *outlen);
/* This is used to clean up the SPNEGO specific data */
void Curl_auth_cleanup_spnego(struct negotiatedata *nego);
#endif /* USE_SPNEGO */
#endif /* HEADER_CURL_VAUTH_H */