summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/curl_sasl.c
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2017-12-06 21:44:09 +0300
committerGeorge Hazan <ghazan@miranda.im>2017-12-06 21:44:17 +0300
commit4b6980f68d25901133519bc1ad1c5376819a3876 (patch)
tree0d919622bfc2659f34a7bed303fefb99ecab052a /libs/libcurl/src/curl_sasl.c
parent0112d2767268037cf63e44c4464cf9eed237d06d (diff)
libcurl: update to 7.57
Diffstat (limited to 'libs/libcurl/src/curl_sasl.c')
-rw-r--r--libs/libcurl/src/curl_sasl.c939
1 files changed, 514 insertions, 425 deletions
diff --git a/libs/libcurl/src/curl_sasl.c b/libs/libcurl/src/curl_sasl.c
index b3ffc66156..550433d674 100644
--- a/libs/libcurl/src/curl_sasl.c
+++ b/libs/libcurl/src/curl_sasl.c
@@ -5,11 +5,11 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
@@ -19,10 +19,13 @@
* KIND, either express or implied.
*
* RFC2195 CRAM-MD5 authentication
+ * RFC2617 Basic and Digest Access Authentication
* RFC2831 DIGEST-MD5 authentication
* RFC4422 Simple Authentication and Security Layer (SASL)
* RFC4616 PLAIN authentication
* RFC6749 OAuth 2.0 Authorization Framework
+ * RFC7628 A Set of SASL Mechanisms for OAuth
+ * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
***************************************************************************/
@@ -33,507 +36,593 @@
#include "curl_base64.h"
#include "curl_md5.h"
-#include "sslgen.h"
+#include "vauth/vauth.h"
+#include "vtls/vtls.h"
#include "curl_hmac.h"
-#include "curl_ntlm_msgs.h"
#include "curl_sasl.h"
#include "warnless.h"
+#include "strtok.h"
+#include "sendf.h"
+#include "non-ascii.h" /* included for Curl_convert_... prototypes */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
#include "curl_memory.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
#include "memdebug.h"
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-/* 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 sasl_digest_get_key_value(const unsigned char *chlg,
- const char *key,
- char *value,
- size_t max_val_len,
- char end_char)
-{
- char *find_pos;
- size_t i;
-
- find_pos = strstr((const char *) 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;
-}
-#endif
+/* Supported mechanisms */
+static const struct {
+ const char *name; /* Name */
+ size_t len; /* Name length */
+ unsigned int bit; /* Flag bit */
+} mechtable[] = {
+ { "LOGIN", 5, SASL_MECH_LOGIN },
+ { "PLAIN", 5, SASL_MECH_PLAIN },
+ { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 },
+ { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 },
+ { "GSSAPI", 6, SASL_MECH_GSSAPI },
+ { "EXTERNAL", 8, SASL_MECH_EXTERNAL },
+ { "NTLM", 4, SASL_MECH_NTLM },
+ { "XOAUTH2", 7, SASL_MECH_XOAUTH2 },
+ { "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER },
+ { ZERO_NULL, 0, 0 }
+};
/*
- * Curl_sasl_create_plain_message()
+ * Curl_sasl_cleanup()
*
- * This is used to generate an already encoded PLAIN message ready
- * for sending to the recipient.
+ * This is used to cleanup any libraries or curl modules used by the sasl
+ * functions.
*
* Parameters:
*
- * data [in] - The session handle.
- * userp [in] - The user name.
- * passdwp [in] - The user's password.
- * 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.
+ * conn [in] - The connection data.
+ * authused [in] - The authentication mechanism used.
*/
-CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data,
- const char *userp,
- const char *passwdp,
- char **outptr, size_t *outlen)
+void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
{
- CURLcode result;
- char *plainauth;
- size_t ulen;
- size_t plen;
-
- ulen = strlen(userp);
- plen = strlen(passwdp);
-
- plainauth = malloc(2 * ulen + plen + 2);
- if(!plainauth) {
- *outlen = 0;
- *outptr = NULL;
- return CURLE_OUT_OF_MEMORY;
+#if defined(USE_KERBEROS5)
+ /* Cleanup the gssapi structure */
+ if(authused == SASL_MECH_GSSAPI) {
+ Curl_auth_gssapi_cleanup(&conn->krb5);
}
+#endif
- /* Calculate the reply */
- memcpy(plainauth, userp, ulen);
- plainauth[ulen] = '\0';
- memcpy(plainauth + ulen + 1, userp, ulen);
- plainauth[2 * ulen + 1] = '\0';
- memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
-
- /* Base64 encode the reply */
- result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr,
- outlen);
- Curl_safefree(plainauth);
- return result;
+#if defined(USE_NTLM)
+ /* Cleanup the NTLM structure */
+ if(authused == SASL_MECH_NTLM) {
+ Curl_auth_ntlm_cleanup(&conn->ntlm);
+ }
+#endif
+
+#if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
+ /* Reserved for future use */
+ (void)conn;
+ (void)authused;
+#endif
}
/*
- * Curl_sasl_create_login_message()
+ * Curl_sasl_decode_mech()
*
- * This is used to generate an already encoded LOGIN message containing the
- * user name or password ready for sending to the recipient.
+ * Convert a SASL mechanism name into a token.
*
* Parameters:
*
- * data [in] - The session handle.
- * valuep [in] - The user name or user's password.
- * 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.
+ * ptr [in] - The mechanism string.
+ * maxlen [in] - Maximum mechanism string length.
+ * len [out] - If not NULL, effective name length.
*
- * Returns CURLE_OK on success.
+ * Returns the SASL mechanism token or 0 if no match.
*/
-CURLcode Curl_sasl_create_login_message(struct SessionHandle *data,
- const char *valuep, char **outptr,
- size_t *outlen)
+unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
{
- size_t vlen = strlen(valuep);
-
- if(!vlen) {
- /* Calculate an empty reply */
- *outptr = strdup("=");
- if(*outptr) {
- *outlen = (size_t) 1;
- return CURLE_OK;
- }
+ unsigned int i;
+ char c;
+
+ for(i = 0; mechtable[i].name; i++) {
+ if(maxlen >= mechtable[i].len &&
+ !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
+ if(len)
+ *len = mechtable[i].len;
+
+ if(maxlen == mechtable[i].len)
+ return mechtable[i].bit;
- *outlen = 0;
- return CURLE_OUT_OF_MEMORY;
+ c = ptr[mechtable[i].len];
+ if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
+ return mechtable[i].bit;
+ }
}
- /* Base64 encode the value */
- return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
+ return 0;
}
-#ifndef CURL_DISABLE_CRYPTO_AUTH
/*
- * Curl_sasl_create_cram_md5_message()
- *
- * This is used to generate an already encoded CRAM-MD5 response message ready
- * for sending to the recipient.
- *
- * Parameters:
+ * Curl_sasl_parse_url_auth_option()
*
- * data [in] - The session handle.
- * chlg64 [in] - Pointer to the base64 encoded challenge buffer.
- * userp [in] - The user name.
- * passdwp [in] - The user's password.
- * 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.
+ * Parse the URL login options.
*/
-CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data,
- const char *chlg64,
- const char *userp,
- const char *passwdp,
- char **outptr, size_t *outlen)
+CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
+ const char *value, size_t len)
{
CURLcode result = CURLE_OK;
- size_t chlg64len = strlen(chlg64);
- unsigned char *chlg = (unsigned char *) NULL;
- size_t chlglen = 0;
- HMAC_context *ctxt;
- unsigned char digest[MD5_DIGEST_LEN];
- char *response;
+ unsigned int mechbit;
+ size_t mechlen;
- /* Decode the challenge if necessary */
- if(chlg64len && *chlg64 != '=') {
- result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+ if(!len)
+ return CURLE_URL_MALFORMAT;
- if(result)
- return result;
+ if(sasl->resetprefs) {
+ sasl->resetprefs = FALSE;
+ sasl->prefmech = SASL_AUTH_NONE;
}
- /* 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) {
- Curl_safefree(chlg);
- return CURLE_OUT_OF_MEMORY;
+ if(!strncmp(value, "*", len))
+ sasl->prefmech = SASL_AUTH_DEFAULT;
+ else {
+ mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
+ if(mechbit && mechlen == len)
+ sasl->prefmech |= mechbit;
+ else
+ result = CURLE_URL_MALFORMAT;
}
- /* Update the digest with the given challenge */
- if(chlglen > 0)
- Curl_HMAC_update(ctxt, chlg, curlx_uztoui(chlglen));
-
- Curl_safefree(chlg);
-
- /* Finalise the digest */
- Curl_HMAC_final(ctxt, digest);
-
- /* Prepare 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;
-
- /* Base64 encode the reply */
- result = Curl_base64_encode(data, response, 0, outptr, outlen);
-
- Curl_safefree(response);
return result;
}
/*
- * Curl_sasl_create_digest_md5_message()
- *
- * This is used to generate an already encoded DIGEST-MD5 response message
- * ready for sending to the recipient.
+ * Curl_sasl_init()
*
- * Parameters:
- *
- * data [in] - The session handle.
- * chlg64 [in] - Pointer to the base64 encoded challenge buffer.
- * userp [in] - The user name.
- * passdwp [in] - The user's password.
- * service [in] - The service type such as www, smtp or pop
- * 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.
+ * Initializes the SASL structure.
*/
-CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
- const char *chlg64,
- const char *userp,
- const char *passwdp,
- const char *service,
- char **outptr, size_t *outlen)
+void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
{
- static const char table16[] = "0123456789abcdef";
-
- CURLcode result = CURLE_OK;
- unsigned char *chlg = (unsigned char *) NULL;
- size_t chlglen = 0;
- size_t i;
- MD5_context *ctxt;
- 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 alg[64];
- char nonceCount[] = "00000001";
- char cnonce[] = "12345678"; /* will be changed */
- char method[] = "AUTHENTICATE";
- char qop[] = "auth";
- char uri[128];
- char response[512];
-
- result = Curl_base64_decode(chlg64, &chlg, &chlglen);
-
- if(result)
- return result;
-
- if(!chlg)
- return CURLE_LOGIN_DENIED;
-
- /* Retrieve nonce string from the challenge */
- if(!sasl_digest_get_key_value(chlg, "nonce=\"", nonce,
- sizeof(nonce), '\"')) {
- Curl_safefree(chlg);
- return CURLE_LOGIN_DENIED;
- }
-
- /* Retrieve realm string from the challenge */
- if(!sasl_digest_get_key_value(chlg, "realm=\"", realm,
- sizeof(realm), '\"')) {
- /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
- strcpy(realm, "");
- }
-
- /* Retrieve algorithm string from the challenge */
- if(!sasl_digest_get_key_value(chlg, "algorithm=", alg, sizeof(alg), ',')) {
- Curl_safefree(chlg);
- return CURLE_LOGIN_DENIED;
- }
-
- Curl_safefree(chlg);
-
- /* We do not support other algorithms */
- if(strcmp(alg, "md5-sess") != 0)
- return CURLE_LOGIN_DENIED;
-
- /* Generate 64 bits of random data */
- for(i = 0; i < 8; i++)
- cnonce[i] = table16[Curl_rand(data)%16];
-
- /* 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++)
- snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
-
- /* Prepare the URL string */
- snprintf(uri, sizeof(uri), "%s/%s", service, realm);
-
- /* Calculate H(A2) */
- ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
- if(!ctxt)
- 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 *) uri,
- curlx_uztoui(strlen(uri)));
- Curl_MD5_final(ctxt, digest);
-
- for(i = 0; i < MD5_DIGEST_LEN; i++)
- snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
-
- /* Now calculate the response hash */
- ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
- if(!ctxt)
- 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++)
- snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
-
- snprintf(response, sizeof(response),
- "username=\"%s\",realm=\"%s\",nonce=\"%s\","
- "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s",
- userp, realm, nonce,
- cnonce, nonceCount, uri, resp_hash_hex);
-
- /* Base64 encode the reply */
- return Curl_base64_encode(data, response, 0, outptr, outlen);
+ sasl->params = params; /* Set protocol dependent parameters */
+ sasl->state = SASL_STOP; /* Not yet running */
+ sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
+ sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
+ sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
+ sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */
+ sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */
+ sasl->force_ir = FALSE; /* Respect external option */
}
-#endif
-#ifdef USE_NTLM
/*
- * Curl_sasl_create_ntlm_type1_message()
- *
- * This is used to generate an already encoded NTLM type-1 message ready for
- * sending to the recipient.
- *
- * Note: This is a simple wrapper of the NTLM function which means that any
- * SASL based protocols don't have to include the NTLM functions directly.
+ * state()
*
- * Parameters:
- *
- * userp [in] - The user name in the format User or Domain\User.
- * passdwp [in] - The user's password.
- * ntlm [in/out] - The ntlm data struct being used and modified.
- * outptr [in/out] - The address where a pointer to newly allocated memory
- * holding the result will be stored upon completion.
- * outlen [out] - The length of the output message.
- *
- * Returns CURLE_OK on success.
+ * This is the ONLY way to change SASL state!
*/
-CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
- const char *passwdp,
- struct ntlmdata *ntlm,
- char **outptr, size_t *outlen)
+static void state(struct SASL *sasl, struct connectdata *conn,
+ saslstate newstate)
{
- return Curl_ntlm_create_type1_message(userp, passwdp, ntlm, outptr,
- outlen);
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[]={
+ "STOP",
+ "PLAIN",
+ "LOGIN",
+ "LOGIN_PASSWD",
+ "EXTERNAL",
+ "CRAMMD5",
+ "DIGESTMD5",
+ "DIGESTMD5_RESP",
+ "NTLM",
+ "NTLM_TYPE2MSG",
+ "GSSAPI",
+ "GSSAPI_TOKEN",
+ "GSSAPI_NO_DATA",
+ "OAUTH2",
+ "OAUTH2_RESP",
+ "CANCEL",
+ "FINAL",
+ /* LAST */
+ };
+
+ if(sasl->state != newstate)
+ infof(conn->data, "SASL %p state change from %s to %s\n",
+ (void *)sasl, names[sasl->state], names[newstate]);
+#else
+ (void) conn;
+#endif
+
+ sasl->state = newstate;
}
/*
- * Curl_sasl_create_ntlm_type3_message()
- *
- * This is used to generate an already encoded NTLM type-3 message ready for
- * sending to the recipient.
- *
- * Parameters:
+ * Curl_sasl_can_authenticate()
*
- * data [in] - Pointer to session handle.
- * header [in] - Pointer to the base64 encoded type-2 message buffer.
- * userp [in] - The user name in the format User or Domain\User.
- * passdwp [in] - The user's password.
- * ntlm [in/out] - The ntlm data struct being used and modified.
- * outptr [in/out] - The address where a pointer to newly allocated memory
- * holding the result will be stored upon completion.
- * outlen [out] - The length of the output message.
- *
- * Returns CURLE_OK on success.
+ * Check if we have enough auth data and capabilities to authenticate.
*/
-CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
- const char *header,
- const char *userp,
- const char *passwdp,
- struct ntlmdata *ntlm,
- char **outptr, size_t *outlen)
+bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
{
- CURLcode result = Curl_ntlm_decode_type2_message(data, header, ntlm);
+ /* Have credentials been provided? */
+ if(conn->bits.user_passwd)
+ return TRUE;
- if(!result)
- result = Curl_ntlm_create_type3_message(data, userp, passwdp, ntlm,
- outptr, outlen);
+ /* EXTERNAL can authenticate without a user name and/or password */
+ if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
+ return TRUE;
- return result;
+ return FALSE;
}
-#endif /* USE_NTLM */
/*
- * Curl_sasl_create_xoauth2_message()
- *
- * This is used to generate an already encoded XOAUTH2 message ready
- * for sending to the recipient.
- *
- * Parameters:
+ * Curl_sasl_start()
*
- * data [in] - The session handle.
- * user [in] - The user name.
- * bearer [in] - The XOAUTH Bearer token.
- * 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.
+ * Calculate the required login details for SASL authentication.
*/
-CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data,
- const char *user,
- const char *bearer,
- char **outptr, size_t *outlen)
+CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
+ bool force_ir, saslprogress *progress)
{
- char *xoauth;
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ unsigned int enabledmechs;
+ const char *mech = NULL;
+ char *resp = NULL;
+ size_t len = 0;
+ saslstate state1 = SASL_STOP;
+ saslstate state2 = SASL_FINAL;
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+ const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
+#if defined(USE_KERBEROS5)
+ const char *service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] :
+ sasl->params->service;
+#endif
- xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
+ sasl->force_ir = force_ir; /* Latch for future use */
+ sasl->authused = 0; /* No mechanism used yet */
+ enabledmechs = sasl->authmechs & sasl->prefmech;
+ *progress = SASL_IDLE;
+
+ /* Calculate the supported authentication mechanism, by decreasing order of
+ security, as well as the initial response where appropriate */
+ if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
+ mech = SASL_MECH_STRING_EXTERNAL;
+ state1 = SASL_EXTERNAL;
+ sasl->authused = SASL_MECH_EXTERNAL;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_external_message(data, conn->user, &resp,
+ &len);
+ }
+ else if(conn->bits.user_passwd) {
+#if defined(USE_KERBEROS5)
+ if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
+ Curl_auth_user_contains_domain(conn->user)) {
+ sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
+ mech = SASL_MECH_STRING_GSSAPI;
+ state1 = SASL_GSSAPI;
+ state2 = SASL_GSSAPI_TOKEN;
+ sasl->authused = SASL_MECH_GSSAPI;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_gssapi_user_message(data, conn->user,
+ conn->passwd,
+ service,
+ data->easy_conn->
+ host.name,
+ sasl->mutual_auth,
+ NULL, &conn->krb5,
+ &resp, &len);
+ }
+ else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
+ Curl_auth_is_digest_supported()) {
+ mech = SASL_MECH_STRING_DIGEST_MD5;
+ state1 = SASL_DIGESTMD5;
+ sasl->authused = SASL_MECH_DIGEST_MD5;
+ }
+ else if(enabledmechs & SASL_MECH_CRAM_MD5) {
+ mech = SASL_MECH_STRING_CRAM_MD5;
+ state1 = SASL_CRAMMD5;
+ sasl->authused = SASL_MECH_CRAM_MD5;
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
+ mech = SASL_MECH_STRING_NTLM;
+ state1 = SASL_NTLM;
+ state2 = SASL_NTLM_TYPE2MSG;
+ sasl->authused = SASL_MECH_NTLM;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_ntlm_type1_message(data,
+ conn->user, conn->passwd,
+ &conn->ntlm, &resp, &len);
+ }
+ else
+#endif
+ if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) {
+ mech = SASL_MECH_STRING_OAUTHBEARER;
+ state1 = SASL_OAUTH2;
+ state2 = SASL_OAUTH2_RESP;
+ sasl->authused = SASL_MECH_OAUTHBEARER;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_oauth_bearer_message(data, conn->user,
+ hostname,
+ port,
+ conn->oauth_bearer,
+ &resp, &len);
+ }
+ else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) {
+ mech = SASL_MECH_STRING_XOAUTH2;
+ state1 = SASL_OAUTH2;
+ sasl->authused = SASL_MECH_XOAUTH2;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_oauth_bearer_message(data, conn->user,
+ NULL, 0,
+ conn->oauth_bearer,
+ &resp, &len);
+ }
+ else if(enabledmechs & SASL_MECH_LOGIN) {
+ mech = SASL_MECH_STRING_LOGIN;
+ state1 = SASL_LOGIN;
+ state2 = SASL_LOGIN_PASSWD;
+ sasl->authused = SASL_MECH_LOGIN;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
+ }
+ else if(enabledmechs & SASL_MECH_PLAIN) {
+ mech = SASL_MECH_STRING_PLAIN;
+ state1 = SASL_PLAIN;
+ sasl->authused = SASL_MECH_PLAIN;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
+ &resp, &len);
+ }
+ }
+
+ if(!result && mech) {
+ if(resp && sasl->params->maxirlen &&
+ strlen(mech) + len > sasl->params->maxirlen) {
+ free(resp);
+ resp = NULL;
+ }
+
+ result = sasl->params->sendauth(conn, mech, resp);
+ if(!result) {
+ *progress = SASL_INPROGRESS;
+ state(sasl, conn, resp ? state2 : state1);
+ }
+ }
- if(!xoauth)
- return CURLE_OUT_OF_MEMORY;
+ free(resp);
- /* Base64 encode the reply */
- return Curl_base64_encode(data, xoauth, strlen(xoauth), outptr,
- outlen);
+ return result;
}
/*
- * Curl_sasl_cleanup()
+ * Curl_sasl_continue()
*
- * This is used to cleanup any libraries or curl modules used by the sasl
- * functions.
- *
- * Parameters:
- *
- * conn [in] - Pointer to the connection data.
- * authused [in] - The authentication mechanism used.
+ * Continue the authentication.
*/
-void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
+CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
+ int code, saslprogress *progress)
{
-#ifdef USE_NTLM
- /* Cleanup the ntlm structure */
- if(authused == SASL_MECH_NTLM) {
- Curl_ntlm_sspi_cleanup(&conn->ntlm);
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ saslstate newstate = SASL_FINAL;
+ char *resp = NULL;
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+ const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+ char *chlg = NULL;
+ size_t chlglen = 0;
+#endif
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5)
+ const char *service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] :
+ sasl->params->service;
+#endif
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \
+ defined(USE_NTLM)
+ char *serverdata;
+#endif
+ size_t len = 0;
+
+ *progress = SASL_INPROGRESS;
+
+ if(sasl->state == SASL_FINAL) {
+ if(code != sasl->params->finalcode)
+ result = CURLE_LOGIN_DENIED;
+ *progress = SASL_DONE;
+ state(sasl, conn, SASL_STOP);
+ return result;
}
- (void)conn;
-#else
- /* Reserved for future use */
- (void)conn;
- (void)authused;
+
+ if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
+ code != sasl->params->contcode) {
+ *progress = SASL_DONE;
+ state(sasl, conn, SASL_STOP);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ switch(sasl->state) {
+ case SASL_STOP:
+ *progress = SASL_DONE;
+ return result;
+ case SASL_PLAIN:
+ result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
+ &resp,
+ &len);
+ break;
+ case SASL_LOGIN:
+ result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
+ newstate = SASL_LOGIN_PASSWD;
+ break;
+ case SASL_LOGIN_PASSWD:
+ result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len);
+ break;
+ case SASL_EXTERNAL:
+ result = Curl_auth_create_external_message(data, conn->user, &resp, &len);
+ break;
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ case SASL_CRAMMD5:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen);
+ if(!result)
+ result = Curl_auth_create_cram_md5_message(data, chlg, conn->user,
+ conn->passwd, &resp, &len);
+ free(chlg);
+ break;
+ case SASL_DIGESTMD5:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_auth_create_digest_md5_message(data, serverdata,
+ conn->user, conn->passwd,
+ service,
+ &resp, &len);
+ newstate = SASL_DIGESTMD5_RESP;
+ break;
+ case SASL_DIGESTMD5_RESP:
+ resp = strdup("");
+ if(!resp)
+ result = CURLE_OUT_OF_MEMORY;
+ break;
#endif
+
+#ifdef USE_NTLM
+ case SASL_NTLM:
+ /* Create the type-1 message */
+ result = Curl_auth_create_ntlm_type1_message(data,
+ conn->user, conn->passwd,
+ &conn->ntlm, &resp, &len);
+ newstate = SASL_NTLM_TYPE2MSG;
+ break;
+ case SASL_NTLM_TYPE2MSG:
+ /* Decode the type-2 message */
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_auth_decode_ntlm_type2_message(data, serverdata,
+ &conn->ntlm);
+ if(!result)
+ result = Curl_auth_create_ntlm_type3_message(data, conn->user,
+ conn->passwd, &conn->ntlm,
+ &resp, &len);
+ break;
+#endif
+
+#if defined(USE_KERBEROS5)
+ case SASL_GSSAPI:
+ result = Curl_auth_create_gssapi_user_message(data, conn->user,
+ conn->passwd,
+ service,
+ data->easy_conn->host.name,
+ sasl->mutual_auth, NULL,
+ &conn->krb5,
+ &resp, &len);
+ newstate = SASL_GSSAPI_TOKEN;
+ break;
+ case SASL_GSSAPI_TOKEN:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ if(sasl->mutual_auth) {
+ /* Decode the user token challenge and create the optional response
+ message */
+ result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
+ NULL, NULL,
+ sasl->mutual_auth,
+ serverdata, &conn->krb5,
+ &resp, &len);
+ newstate = SASL_GSSAPI_NO_DATA;
+ }
+ else
+ /* Decode the security challenge and create the response message */
+ result = Curl_auth_create_gssapi_security_message(data, serverdata,
+ &conn->krb5,
+ &resp, &len);
+ break;
+ case SASL_GSSAPI_NO_DATA:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ /* Decode the security challenge and create the response message */
+ result = Curl_auth_create_gssapi_security_message(data, serverdata,
+ &conn->krb5,
+ &resp, &len);
+ break;
+#endif
+
+ case SASL_OAUTH2:
+ /* Create the authorisation message */
+ if(sasl->authused == SASL_MECH_OAUTHBEARER) {
+ result = Curl_auth_create_oauth_bearer_message(data, conn->user,
+ hostname,
+ port,
+ conn->oauth_bearer,
+ &resp, &len);
+
+ /* Failures maybe sent by the server as continuations for OAUTHBEARER */
+ newstate = SASL_OAUTH2_RESP;
+ }
+ else
+ result = Curl_auth_create_oauth_bearer_message(data, conn->user,
+ NULL, 0,
+ conn->oauth_bearer,
+ &resp, &len);
+ break;
+
+ case SASL_OAUTH2_RESP:
+ /* The continuation is optional so check the response code */
+ if(code == sasl->params->finalcode) {
+ /* Final response was received so we are done */
+ *progress = SASL_DONE;
+ state(sasl, conn, SASL_STOP);
+ return result;
+ }
+ else if(code == sasl->params->contcode) {
+ /* Acknowledge the continuation by sending a 0x01 response base64
+ encoded */
+ resp = strdup("AQ==");
+ if(!resp)
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ else {
+ *progress = SASL_DONE;
+ state(sasl, conn, SASL_STOP);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ case SASL_CANCEL:
+ /* Remove the offending mechanism from the supported list */
+ sasl->authmechs ^= sasl->authused;
+
+ /* Start an alternative SASL authentication */
+ result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
+ newstate = sasl->state; /* Use state from Curl_sasl_start() */
+ break;
+ default:
+ failf(data, "Unsupported SASL authentication mechanism");
+ result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
+ break;
+ }
+
+ switch(result) {
+ case CURLE_BAD_CONTENT_ENCODING:
+ /* Cancel dialog */
+ result = sasl->params->sendcont(conn, "*");
+ newstate = SASL_CANCEL;
+ break;
+ case CURLE_OK:
+ if(resp)
+ result = sasl->params->sendcont(conn, resp);
+ break;
+ default:
+ newstate = SASL_STOP; /* Stop on error */
+ *progress = SASL_DONE;
+ break;
+ }
+
+ free(resp);
+
+ state(sasl, conn, newstate);
+
+ return result;
}