mirror of
https://github.com/curl/curl.git
synced 2026-07-29 23:28:08 +03:00
auth: add support for RFC7616 - HTTP Digest access authentication
Signed-off-by: Florin <petriuc.florin@gmail.com>
This commit is contained in:
parent
1d0c8dea95
commit
2b5b37cb91
7 changed files with 455 additions and 46 deletions
|
|
@ -19,6 +19,7 @@
|
|||
* KIND, either express or implied.
|
||||
*
|
||||
* RFC2831 DIGEST-MD5 authentication
|
||||
* RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
|
|
@ -34,6 +35,7 @@
|
|||
#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"
|
||||
|
|
@ -144,6 +146,15 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
|
|||
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)
|
||||
{
|
||||
|
|
@ -602,9 +613,22 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
|
|||
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! */
|
||||
}
|
||||
|
|
@ -635,7 +659,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
|
|||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
|
@ -654,20 +678,24 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
|
|||
*
|
||||
* 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;
|
||||
|
|
@ -692,6 +720,17 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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):
|
||||
|
||||
|
|
@ -703,26 +742,29 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -738,27 +780,32 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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,
|
||||
|
|
@ -767,19 +814,19 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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)
|
||||
|
||||
|
|
@ -794,7 +841,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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;
|
||||
|
||||
|
|
@ -858,6 +905,16 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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);
|
||||
|
|
@ -865,6 +922,58 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
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.
|
||||
* 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()
|
||||
*
|
||||
|
|
@ -887,6 +996,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest)
|
|||
digest->nc = 0;
|
||||
digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
|
||||
digest->stale = FALSE; /* default means normal, not stale */
|
||||
digest->userhash = FALSE;
|
||||
}
|
||||
#endif /* !USE_WINDOWS_SSPI */
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@
|
|||
|
||||
enum {
|
||||
CURLDIGESTALGO_MD5,
|
||||
CURLDIGESTALGO_MD5SESS
|
||||
CURLDIGESTALGO_MD5SESS,
|
||||
CURLDIGESTALGO_SHA256,
|
||||
CURLDIGESTALGO_SHA256SESS,
|
||||
CURLDIGESTALGO_SHA512_256,
|
||||
CURLDIGESTALGO_SHA512_256SESS
|
||||
};
|
||||
|
||||
/* This is used to extract the realm from a challenge message */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue