* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 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
* KIND, either express or implied.
*
* RFC2831 DIGEST-MD5 authentication
+ * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
*
***************************************************************************/
#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"
snprintf((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++)
+ snprintf((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 *tmp;
char *token;
- char *tok_buf;
+ char *tok_buf = NULL;
/* Initialise the output */
*value = 0;
* auth_decode_digest_md5_message()
*
* This is used internally to decode an already encoded DIGEST-MD5 challenge
- * message into the seperate attributes.
+ * message into the separate attributes.
*
* Parameters:
*
char qop_options[64];
int qop_values;
char cnonce[33];
- unsigned int entropy[4];
char nonceCount[] = "00000001";
char method[] = "AUTHENTICATE";
char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
char *spn = NULL;
- /* Decode the challange message */
+ /* Decode the challenge message */
result = auth_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
realm, sizeof(realm),
algorithm, sizeof(algorithm),
if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
return CURLE_BAD_CONTENT_ENCODING;
- /* Generate 16 bytes of random data */
- result = Curl_rand(data, &entropy[0], 4);
+ /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
+ result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
if(result)
return result;
- /* Convert the random data into a 32 byte hex string */
- snprintf(cnonce, sizeof(cnonce), "%08x%08x%08x%08x",
- entropy[0], entropy[1], entropy[2], entropy[3]);
-
/* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
if(!ctxt)
/*
* Curl_auth_decode_digest_http_message()
*
- * This is used to decode a HTTP DIGEST challenge message into the seperate
+ * This is used to decode a HTTP DIGEST challenge message into the separate
* attributes.
*
* Parameters:
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "qop")) {
- char *tok_buf;
+ 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);
digest->algo = CURLDIGESTALGO_MD5SESS;
else if(strcasecompare(content, "MD5"))
digest->algo = CURLDIGESTALGO_MD5;
+ else if(strcasecompare(content, "SHA-256"))
+ digest->algo = CURLDIGESTALGO_SHA256;
+ else if(strcasecompare(content, "SHA-256-SESS"))
+ digest->algo = CURLDIGESTALGO_SHA256SESS;
+ else if(strcasecompare(content, "SHA-512-256"))
+ digest->algo = CURLDIGESTALGO_SHA512_256;
+ else if(strcasecompare(content, "SHA-512-256-SESS"))
+ digest->algo = CURLDIGESTALGO_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! */
}
}
/*
- * Curl_auth_create_digest_http_message()
+ * _Curl_auth_create_digest_http_message()
*
* This is used to generate a HTTP DIGEST response message ready for sending
* to the recipient.
*
* 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)
+static 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,
+ void (*convert_to_ascii)(unsigned char *, unsigned char *),
+ void (*hash)(unsigned char *, const unsigned char *))
{
CURLcode result;
- unsigned char md5buf[16]; /* 16 bytes/128 bits */
- unsigned char request_digest[33];
- unsigned char *md5this;
- unsigned char ha1[33]; /* 32 digits and 1 zero byte */
- unsigned char ha2[33]; /* 32 digits and 1 zero byte */
+ unsigned char hashbuf[32]; /* 32 bytes/256 bits */
+ unsigned char request_digest[65];
+ unsigned char *hashthis;
+ unsigned char ha1[65]; /* 64 digits and 1 zero byte */
+ unsigned char ha2[65]; /* 64 digits and 1 zero byte */
+ char userh[65];
char cnoncebuf[33];
char *cnonce = NULL;
size_t cnonce_sz = 0;
digest->nc = 1;
if(!digest->cnonce) {
- unsigned int rnd[4];
- result = Curl_rand(data, &rnd[0], 4);
+ result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
+ sizeof(cnoncebuf));
if(result)
return result;
- snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x",
- rnd[0], rnd[1], rnd[2], rnd[3]);
result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
&cnonce, &cnonce_sz);
digest->cnonce = cnonce;
}
+ if(digest->userhash) {
+ hashthis = (unsigned char *) aprintf("%s:%s", userp, digest->realm);
+ if(!hashthis)
+ return CURLE_OUT_OF_MEMORY;
+
+ CURL_OUTPUT_DIGEST_CONV(data, hashthis);
+ hash(hashbuf, hashthis);
+ free(hashthis);
+ convert_to_ascii(hashbuf, (unsigned char *)userh);
+ }
+
/*
If the algorithm is "MD5" or unspecified (which then defaults to MD5):
unq(nonce-value) ":" unq(cnonce-value)
*/
- md5this = (unsigned char *)
- aprintf("%s:%s:%s", userp, digest->realm, passwdp);
- if(!md5this)
+ hashthis = (unsigned char *)
+ aprintf("%s:%s:%s", digest->userhash ? userh : userp,
+ digest->realm, passwdp);
+ if(!hashthis)
return CURLE_OUT_OF_MEMORY;
- CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
- Curl_md5it(md5buf, md5this);
- free(md5this);
- auth_digest_md5_to_ascii(md5buf, ha1);
+ CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
+ hash(hashbuf, hashthis);
+ free(hashthis);
+ convert_to_ascii(hashbuf, ha1);
- if(digest->algo == CURLDIGESTALGO_MD5SESS) {
+ if(digest->algo == CURLDIGESTALGO_MD5SESS ||
+ digest->algo == CURLDIGESTALGO_SHA256SESS ||
+ digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
/* nonce and cnonce are OUTSIDE the hash */
tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
- Curl_md5it(md5buf, (unsigned char *) tmp);
+ hash(hashbuf, (unsigned char *) tmp);
free(tmp);
- auth_digest_md5_to_ascii(md5buf, ha1);
+ convert_to_ascii(hashbuf, ha1);
}
/*
5.1.1 of RFC 2616)
*/
- md5this = (unsigned char *) aprintf("%s:%s", request, uripath);
+ hashthis = (unsigned char *) aprintf("%s:%s", request, uripath);
if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
/* We don't support auth-int for PUT or POST at the moment.
- TODO: replace md5 of empty string with entity-body for PUT/POST */
- unsigned char *md5this2 = (unsigned char *)
- aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
- free(md5this);
- md5this = md5this2;
+ TODO: replace hash of empty string with entity-body for PUT/POST */
+ char hashed[65];
+ unsigned char *hashthis2;
+
+ hash(hashbuf, (const unsigned char *)"");
+ convert_to_ascii(hashbuf, (unsigned char *)hashed);
+
+ hashthis2 = (unsigned char *)aprintf("%s:%s", hashthis, hashed);
+ free(hashthis);
+ hashthis = hashthis2;
}
- if(!md5this)
+ if(!hashthis)
return CURLE_OUT_OF_MEMORY;
- CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
- Curl_md5it(md5buf, md5this);
- free(md5this);
- auth_digest_md5_to_ascii(md5buf, ha2);
+ CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
+ hash(hashbuf, hashthis);
+ free(hashthis);
+ convert_to_ascii(hashbuf, ha2);
if(digest->qop) {
- md5this = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
+ hashthis = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
ha1,
digest->nonce,
digest->nc,
ha2);
}
else {
- md5this = (unsigned char *) aprintf("%s:%s:%s",
+ hashthis = (unsigned char *) aprintf("%s:%s:%s",
ha1,
digest->nonce,
ha2);
}
- if(!md5this)
+ if(!hashthis)
return CURLE_OUT_OF_MEMORY;
- CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
- Curl_md5it(md5buf, md5this);
- free(md5this);
- auth_digest_md5_to_ascii(md5buf, request_digest);
+ CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
+ hash(hashbuf, hashthis);
+ free(hashthis);
+ convert_to_ascii(hashbuf, request_digest);
/* For test case 64 (snooped from a Mozilla 1.3a request)
characters. algorithm and qop with standard values only contain web-safe
characters.
*/
- userp_quoted = auth_digest_string_quoted(userp);
+ userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
if(!userp_quoted)
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);
}
/*
+ * 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.
+ * passdwp [in] - The user's password.
+ * request [in] - The HTTP request.
+ * uripath [in] - The path of the HTTP uri.
+ * digest [in/out] - The digest data struct being used and modified.
+ * outptr [in/out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen [out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_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)
+{
+ switch(digest->algo) {
+ case CURLDIGESTALGO_MD5:
+ case CURLDIGESTALGO_MD5SESS:
+ return _Curl_auth_create_digest_http_message(data, userp, passwdp,
+ request, uripath, digest,
+ outptr, outlen,
+ auth_digest_md5_to_ascii,
+ Curl_md5it);
+
+ case CURLDIGESTALGO_SHA256:
+ case CURLDIGESTALGO_SHA256SESS:
+ case CURLDIGESTALGO_SHA512_256:
+ case CURLDIGESTALGO_SHA512_256SESS:
+ return _Curl_auth_create_digest_http_message(data, userp, passwdp,
+ request, uripath, digest,
+ outptr, outlen,
+ auth_digest_sha256_to_ascii,
+ Curl_sha256it);
+
+ default:
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+}
+
+/*
* Curl_auth_digest_cleanup()
*
* This is used to clean up the digest specific data.
digest->nc = 0;
digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
digest->stale = FALSE; /* default means normal, not stale */
+ digest->userhash = FALSE;
}
#endif /* !USE_WINDOWS_SSPI */