diff options
Diffstat (limited to 'libs/libcurl/src/vauth')
-rw-r--r-- | libs/libcurl/src/vauth/cleartext.c | 276 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/cram.c | 194 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/digest.c | 1988 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/digest.h | 80 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/digest_sspi.c | 1336 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/gsasl.c | 254 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/krb5_gssapi.c | 646 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/krb5_sspi.c | 948 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/ntlm.c | 1580 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/ntlm.h | 289 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/ntlm_sspi.c | 744 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/oauth2.c | 214 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/spnego_gssapi.c | 562 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/spnego_sspi.c | 728 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/vauth.c | 326 | ||||
-rw-r--r-- | libs/libcurl/src/vauth/vauth.h | 476 |
16 files changed, 5318 insertions, 5323 deletions
diff --git a/libs/libcurl/src/vauth/cleartext.c b/libs/libcurl/src/vauth/cleartext.c index b82b171467..f1db19adbf 100644 --- a/libs/libcurl/src/vauth/cleartext.c +++ b/libs/libcurl/src/vauth/cleartext.c @@ -1,138 +1,138 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) - -#include <curl/curl.h> -#include "urldata.h" - -#include "vauth/vauth.h" -#include "curl_md5.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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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)
+
+#include <curl/curl.h>
+#include "urldata.h"
+
+#include "vauth/vauth.h"
+#include "curl_md5.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 */
diff --git a/libs/libcurl/src/vauth/cram.c b/libs/libcurl/src/vauth/cram.c index 475d31b8d7..9c0c743f37 100644 --- a/libs/libcurl/src/vauth/cram.c +++ b/libs/libcurl/src/vauth/cram.c @@ -1,97 +1,97 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#if !defined(CURL_DISABLE_CRYPTO_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_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#if !defined(CURL_DISABLE_CRYPTO_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_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/vauth/digest.c b/libs/libcurl/src/vauth/digest.c index f945e8b6c9..995aeadc78 100644 --- a/libs/libcurl/src/vauth/digest.c +++ b/libs/libcurl/src/vauth/digest.c @@ -1,994 +1,994 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#if !defined(CURL_DISABLE_CRYPTO_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; - break; - } - } - - 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, realm, 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 a 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 a 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; - - 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 a 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_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#if !defined(CURL_DISABLE_CRYPTO_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;
+ break;
+ }
+ }
+
+ 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, realm, 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;
+
+ 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_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/vauth/digest.h b/libs/libcurl/src/vauth/digest.h index d785bdd91b..e994bb924c 100644 --- a/libs/libcurl/src/vauth/digest.h +++ b/libs/libcurl/src/vauth/digest.h @@ -1,40 +1,40 @@ -#ifndef HEADER_CURL_DIGEST_H -#define HEADER_CURL_DIGEST_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> - -#if !defined(CURL_DISABLE_CRYPTO_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 */ +#ifndef HEADER_CURL_DIGEST_H
+#define HEADER_CURL_DIGEST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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>
+
+#if !defined(CURL_DISABLE_CRYPTO_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 */
diff --git a/libs/libcurl/src/vauth/digest_sspi.c b/libs/libcurl/src/vauth/digest_sspi.c index 89a9db52c7..0a071fe1e5 100644 --- a/libs/libcurl/src/vauth/digest_sspi.c +++ b/libs/libcurl/src/vauth/digest_sspi.c @@ -1,668 +1,668 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2022, 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_CRYPTO_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 a 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 a 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_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2015 - 2022, 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_CRYPTO_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_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/vauth/gsasl.c b/libs/libcurl/src/vauth/gsasl.c index a73c644434..df9db426e1 100644 --- a/libs/libcurl/src/vauth/gsasl.c +++ b/libs/libcurl/src/vauth/gsasl.c @@ -1,127 +1,127 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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 +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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
diff --git a/libs/libcurl/src/vauth/krb5_gssapi.c b/libs/libcurl/src/vauth/krb5_gssapi.c index bac78049dc..c4decbd951 100644 --- a/libs/libcurl/src/vauth/krb5_gssapi.c +++ b/libs/libcurl/src/vauth/krb5_gssapi.c @@ -1,323 +1,323 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2022, 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2015 - 2022, 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 */
diff --git a/libs/libcurl/src/vauth/krb5_sspi.c b/libs/libcurl/src/vauth/krb5_sspi.c index 895b4a1937..44add314be 100644 --- a/libs/libcurl/src/vauth/krb5_sspi.c +++ b/libs/libcurl/src/vauth/krb5_sspi.c @@ -1,474 +1,474 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2022, 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*/ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2022, 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 */
diff --git a/libs/libcurl/src/vauth/ntlm.c b/libs/libcurl/src/vauth/ntlm.c index c10fa6caaf..a93ccf6410 100644 --- a/libs/libcurl/src/vauth/ntlm.c +++ b/libs/libcurl/src/vauth/ntlm.c @@ -1,791 +1,789 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* SSL backend-specific #if branches in this file must be kept in the order - documented in curl_ntlm_core. */ -#if defined(NTLM_NEEDS_NSS_INIT) -#include "vtls/nssg.h" /* for Curl_nss_force_init() */ -#endif - -#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_NETWARE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE "); - 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(NTLM_NEEDS_NSS_INIT) - /* Make sure the crypto backend is initialized */ - result = Curl_nss_force_init(data); - if(result) - return result; -#elif 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; - - 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/* SSL backend-specific #if branches in this file must be kept in the order
+ documented in curl_ntlm_core. */
+#if defined(NTLM_NEEDS_NSS_INIT)
+#include "vtls/nssg.h" /* for Curl_nss_force_init() */
+#endif
+
+#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(NTLM_NEEDS_NSS_INIT)
+ /* Make sure the crypto backend is initialized */
+ result = Curl_nss_force_init(data);
+ if(result)
+ return result;
+#elif 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;
+
+ 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 */
diff --git a/libs/libcurl/src/vauth/ntlm.h b/libs/libcurl/src/vauth/ntlm.h index 4dfda55453..2c8242c1a1 100644 --- a/libs/libcurl/src/vauth/ntlm.h +++ b/libs/libcurl/src/vauth/ntlm.h @@ -1,146 +1,143 @@ -#ifndef HEADER_VAUTH_NTLM_H -#define HEADER_VAUTH_NTLM_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NETWARE (1<<8) -/* unknown purpose */ - -#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 */ +#ifndef HEADER_VAUTH_NTLM_H
+#define HEADER_VAUTH_NTLM_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/vauth/ntlm_sspi.c b/libs/libcurl/src/vauth/ntlm_sspi.c index 193576acaa..12f4e23b43 100644 --- a/libs/libcurl/src/vauth/ntlm_sspi.c +++ b/libs/libcurl/src/vauth/ntlm_sspi.c @@ -1,372 +1,372 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/vauth/oauth2.c b/libs/libcurl/src/vauth/oauth2.c index 1604b303e0..d424b26fdd 100644 --- a/libs/libcurl/src/vauth/oauth2.c +++ b/libs/libcurl/src/vauth/oauth2.c @@ -1,107 +1,107 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) - -#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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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)
+
+#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 */
diff --git a/libs/libcurl/src/vauth/spnego_gssapi.c b/libs/libcurl/src/vauth/spnego_gssapi.c index 25dff967d1..7445b1bdbf 100644 --- a/libs/libcurl/src/vauth/spnego_gssapi.c +++ b/libs/libcurl/src/vauth/spnego_gssapi.c @@ -1,281 +1,281 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/vauth/spnego_sspi.c b/libs/libcurl/src/vauth/spnego_sspi.c index d845caca6c..8efcd2693c 100644 --- a/libs/libcurl/src/vauth/spnego_sspi.c +++ b/libs/libcurl/src/vauth/spnego_sspi.c @@ -1,364 +1,364 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/vauth/vauth.c b/libs/libcurl/src/vauth/vauth.c index 58fe05139d..8f83f60aa3 100644 --- a/libs/libcurl/src/vauth/vauth.c +++ b/libs/libcurl/src/vauth/vauth.c @@ -1,163 +1,163 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2022, 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))); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2022, 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)));
+}
diff --git a/libs/libcurl/src/vauth/vauth.h b/libs/libcurl/src/vauth/vauth.h index af27f01dfb..a781fb9fcc 100644 --- a/libs/libcurl/src/vauth/vauth.h +++ b/libs/libcurl/src/vauth/vauth.h @@ -1,238 +1,238 @@ -#ifndef HEADER_CURL_VAUTH_H -#define HEADER_CURL_VAUTH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2022, 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_CRYPTO_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); - -#if !defined(CURL_DISABLE_CRYPTO_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 a HTTP DIGEST challenge message */ -CURLcode Curl_auth_decode_digest_http_message(const char *chlg, - struct digestdata *digest); - -/* This is used to generate a 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_CRYPTO_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 *passwood, - 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 */ +#ifndef HEADER_CURL_VAUTH_H
+#define HEADER_CURL_VAUTH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2022, 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_CRYPTO_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);
+
+#if !defined(CURL_DISABLE_CRYPTO_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_CRYPTO_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 *passwood,
+ 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 */
|