mirror of
https://github.com/curl/curl.git
synced 2026-04-14 21:51:41 +03:00
digest: handle quotes in the path
- The 'uri' component needs to be escaped as well - Rewrote the quote function to use dynbuf - Build the digest at least partly with dynbuf - Use goto as a general error mechanism - Make test 64 use a double quote in the URL Closes #20295
This commit is contained in:
parent
2949faa93c
commit
134fb66121
2 changed files with 146 additions and 145 deletions
|
|
@ -148,35 +148,26 @@ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
|
|||
}
|
||||
|
||||
/* Perform quoted-string escaping as described in RFC2616 and its errata */
|
||||
static char *auth_digest_string_quoted(const char *source)
|
||||
static char *auth_digest_string_quoted(const char *s)
|
||||
{
|
||||
char *dest;
|
||||
const char *s = source;
|
||||
size_t n = 1; /* null-terminator */
|
||||
|
||||
/* Calculate size needed */
|
||||
struct dynbuf out;
|
||||
curlx_dyn_init(&out, 2048);
|
||||
if(!*s) /* for zero length input, make sure we return an empty string */
|
||||
return curlx_strdup("");
|
||||
while(*s) {
|
||||
++n;
|
||||
CURLcode result;
|
||||
if(*s == '"' || *s == '\\') {
|
||||
++n;
|
||||
result = curlx_dyn_addn(&out, "\\", 1);
|
||||
if(!result)
|
||||
result = curlx_dyn_addn(&out, s, 1);
|
||||
}
|
||||
++s;
|
||||
else
|
||||
result = curlx_dyn_addn(&out, s, 1);
|
||||
if(result)
|
||||
return NULL;
|
||||
s++;
|
||||
}
|
||||
|
||||
dest = curlx_malloc(n);
|
||||
if(dest) {
|
||||
char *d = dest;
|
||||
s = source;
|
||||
while(*s) {
|
||||
if(*s == '"' || *s == '\\') {
|
||||
*d++ = '\\';
|
||||
}
|
||||
*d++ = *s++;
|
||||
}
|
||||
*d = '\0';
|
||||
}
|
||||
|
||||
return dest;
|
||||
return curlx_dyn_ptr(&out);
|
||||
}
|
||||
|
||||
/* Retrieves the value for a corresponding key from the challenge string
|
||||
|
|
@ -672,16 +663,15 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
|
|||
* 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))
|
||||
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 */
|
||||
|
|
@ -691,12 +681,15 @@ static CURLcode auth_create_digest_http_message(
|
|||
char userh[65];
|
||||
char *cnonce = NULL;
|
||||
size_t cnonce_sz = 0;
|
||||
char *userp_quoted;
|
||||
char *realm_quoted;
|
||||
char *nonce_quoted;
|
||||
char *response = NULL;
|
||||
char *userp_quoted = NULL;
|
||||
char *realm_quoted = NULL;
|
||||
char *nonce_quoted = NULL;
|
||||
char *hashthis = NULL;
|
||||
char *tmp = NULL;
|
||||
char *uri_quoted = NULL;
|
||||
struct dynbuf response;
|
||||
*outptr = NULL;
|
||||
|
||||
curlx_dyn_init(&response, 4096); /* arbitrary max */
|
||||
|
||||
memset(hashbuf, 0, sizeof(hashbuf));
|
||||
if(!digest->nc)
|
||||
|
|
@ -710,27 +703,27 @@ static CURLcode auth_create_digest_http_message(
|
|||
#endif
|
||||
(unsigned char *)cnoncebuf,
|
||||
sizeof(cnoncebuf));
|
||||
if(!result)
|
||||
result = curlx_base64_encode((uint8_t *)cnoncebuf, sizeof(cnoncebuf),
|
||||
&cnonce, &cnonce_sz);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = curlx_base64_encode((uint8_t *)cnoncebuf, sizeof(cnoncebuf),
|
||||
&cnonce, &cnonce_sz);
|
||||
if(result)
|
||||
return result;
|
||||
goto oom;
|
||||
|
||||
digest->cnonce = cnonce;
|
||||
}
|
||||
|
||||
if(digest->userhash) {
|
||||
hashthis = curl_maprintf("%s:%s", userp,
|
||||
digest->realm ? digest->realm : "");
|
||||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
char *hasht = curl_maprintf("%s:%s", userp,
|
||||
digest->realm ? digest->realm : "");
|
||||
if(!hasht) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis));
|
||||
curlx_free(hashthis);
|
||||
result = hash(hashbuf, (unsigned char *)hasht, strlen(hasht));
|
||||
curlx_free(hasht);
|
||||
if(result)
|
||||
return result;
|
||||
goto oom;
|
||||
convert_to_ascii(hashbuf, (unsigned char *)userh);
|
||||
}
|
||||
|
||||
|
|
@ -745,27 +738,31 @@ static CURLcode auth_create_digest_http_message(
|
|||
unq(nonce-value) ":" unq(cnonce-value)
|
||||
*/
|
||||
|
||||
hashthis = curl_maprintf("%s:%s:%s", userp,
|
||||
digest->realm ? digest->realm : "", passwdp);
|
||||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
hashthis = curl_maprintf("%s:%s:%s", userp, digest->realm ?
|
||||
digest->realm : "", passwdp);
|
||||
if(!hashthis) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis));
|
||||
curlx_free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
goto oom;
|
||||
convert_to_ascii(hashbuf, ha1);
|
||||
|
||||
if(digest->algo & SESSION_ALGO) {
|
||||
/* nonce and cnonce are OUTSIDE the hash */
|
||||
tmp = curl_maprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
|
||||
if(!tmp)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
char *tmp = curl_maprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
|
||||
if(!tmp) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
result = hash(hashbuf, (unsigned char *)tmp, strlen(tmp));
|
||||
curlx_free(tmp);
|
||||
if(result)
|
||||
return result;
|
||||
goto oom;
|
||||
convert_to_ascii(hashbuf, ha1);
|
||||
}
|
||||
|
||||
|
|
@ -782,9 +779,17 @@ static CURLcode auth_create_digest_http_message(
|
|||
5.1.1 of RFC 2616)
|
||||
*/
|
||||
|
||||
uri_quoted = auth_digest_string_quoted((const char *)uripath);
|
||||
if(!uri_quoted) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
hashthis = curl_maprintf("%s:%s", request, uripath);
|
||||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
if(!hashthis) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if(digest->qop && curl_strequal(digest->qop, "auth-int")) {
|
||||
/* We do not support auth-int for PUT or POST */
|
||||
|
|
@ -792,41 +797,40 @@ static CURLcode auth_create_digest_http_message(
|
|||
char *hashthis2;
|
||||
|
||||
result = hash(hashbuf, (const unsigned char *)"", 0);
|
||||
if(result) {
|
||||
curlx_free(hashthis);
|
||||
return result;
|
||||
}
|
||||
if(result)
|
||||
goto oom;
|
||||
convert_to_ascii(hashbuf, (unsigned char *)hashed);
|
||||
|
||||
hashthis2 = curl_maprintf("%s:%s", hashthis, hashed);
|
||||
curlx_free(hashthis);
|
||||
hashthis = hashthis2;
|
||||
if(!hashthis) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis));
|
||||
curlx_free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
goto oom;
|
||||
convert_to_ascii(hashbuf, ha2);
|
||||
|
||||
if(digest->qop) {
|
||||
if(digest->qop)
|
||||
hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce,
|
||||
digest->nc, digest->cnonce, digest->qop, ha2);
|
||||
}
|
||||
else {
|
||||
else
|
||||
hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2);
|
||||
}
|
||||
|
||||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
if(!hashthis) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
result = hash(hashbuf, (unsigned char *)hashthis, strlen(hashthis));
|
||||
curlx_free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
goto oom;
|
||||
convert_to_ascii(hashbuf, request_digest);
|
||||
|
||||
/* For test case 64 (snooped from a Mozilla 1.3a request)
|
||||
|
|
@ -843,8 +847,10 @@ static CURLcode auth_create_digest_http_message(
|
|||
characters.
|
||||
*/
|
||||
userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
|
||||
if(!userp_quoted)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
if(!userp_quoted) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
if(digest->realm)
|
||||
realm_quoted = auth_digest_string_quoted(digest->realm);
|
||||
else {
|
||||
|
|
@ -853,98 +859,93 @@ static CURLcode auth_create_digest_http_message(
|
|||
realm_quoted[0] = 0;
|
||||
}
|
||||
if(!realm_quoted) {
|
||||
curlx_free(userp_quoted);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
nonce_quoted = auth_digest_string_quoted(digest->nonce);
|
||||
if(!nonce_quoted) {
|
||||
curlx_free(realm_quoted);
|
||||
curlx_free(userp_quoted);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if(digest->qop) {
|
||||
response = curl_maprintf("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);
|
||||
result = curlx_dyn_addf(&response, "username=\"%s\", "
|
||||
"realm=\"%s\", "
|
||||
"nonce=\"%s\", "
|
||||
"uri=\"%s\", "
|
||||
"cnonce=\"%s\", "
|
||||
"nc=%08x, "
|
||||
"qop=%s, "
|
||||
"response=\"%s\"",
|
||||
userp_quoted,
|
||||
realm_quoted,
|
||||
nonce_quoted,
|
||||
uri_quoted,
|
||||
digest->cnonce,
|
||||
digest->nc,
|
||||
digest->qop,
|
||||
request_digest);
|
||||
|
||||
/* Increment nonce-count to use another nc value for the next request */
|
||||
digest->nc++;
|
||||
}
|
||||
else {
|
||||
response = curl_maprintf("username=\"%s\", "
|
||||
"realm=\"%s\", "
|
||||
"nonce=\"%s\", "
|
||||
"uri=\"%s\", "
|
||||
"response=\"%s\"",
|
||||
userp_quoted,
|
||||
realm_quoted,
|
||||
nonce_quoted,
|
||||
uripath,
|
||||
request_digest);
|
||||
result = curlx_dyn_addf(&response, "username=\"%s\", "
|
||||
"realm=\"%s\", "
|
||||
"nonce=\"%s\", "
|
||||
"uri=\"%s\", "
|
||||
"response=\"%s\"",
|
||||
userp_quoted,
|
||||
realm_quoted,
|
||||
nonce_quoted,
|
||||
uri_quoted,
|
||||
request_digest);
|
||||
}
|
||||
curlx_free(nonce_quoted);
|
||||
curlx_free(realm_quoted);
|
||||
curlx_free(userp_quoted);
|
||||
if(!response)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
if(result)
|
||||
goto oom;
|
||||
|
||||
/* Add the optional fields */
|
||||
if(digest->opaque) {
|
||||
char *opaque_quoted;
|
||||
/* Append the opaque */
|
||||
opaque_quoted = auth_digest_string_quoted(digest->opaque);
|
||||
char *opaque_quoted = auth_digest_string_quoted(digest->opaque);
|
||||
if(!opaque_quoted) {
|
||||
curlx_free(response);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto oom;
|
||||
}
|
||||
tmp = curl_maprintf("%s, opaque=\"%s\"", response, opaque_quoted);
|
||||
curlx_free(response);
|
||||
result = curlx_dyn_addf(&response, ", opaque=\"%s\"", opaque_quoted);
|
||||
curlx_free(opaque_quoted);
|
||||
if(!tmp)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
response = tmp;
|
||||
if(result)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if(digest->algorithm) {
|
||||
/* Append the algorithm */
|
||||
tmp = curl_maprintf("%s, algorithm=%s", response, digest->algorithm);
|
||||
curlx_free(response);
|
||||
if(!tmp)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
response = tmp;
|
||||
result = curlx_dyn_addf(&response, ", algorithm=%s", digest->algorithm);
|
||||
if(result)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if(digest->userhash) {
|
||||
/* Append the userhash */
|
||||
tmp = curl_maprintf("%s, userhash=true", response);
|
||||
curlx_free(response);
|
||||
if(!tmp)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
response = tmp;
|
||||
result = curlx_dyn_add(&response, ", userhash=true");
|
||||
if(result)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
/* Return the output */
|
||||
*outptr = response;
|
||||
*outlen = strlen(response);
|
||||
*outptr = curlx_dyn_ptr(&response);
|
||||
*outlen = curlx_dyn_len(&response);
|
||||
result = CURLE_OK;
|
||||
|
||||
return CURLE_OK;
|
||||
oom:
|
||||
curlx_free(nonce_quoted);
|
||||
curlx_free(realm_quoted);
|
||||
curlx_free(uri_quoted);
|
||||
curlx_free(userp_quoted);
|
||||
if(result)
|
||||
curlx_dyn_free(&response);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -61,21 +61,21 @@ digest
|
|||
HTTP with Digest authorization
|
||||
</name>
|
||||
<command>
|
||||
http://%HOSTIP:%HTTPPORT/%TESTNUMBER -u testuser:testpass --digest
|
||||
'http://%HOSTIP:%HTTPPORT/%TESTNUMBER"' -u testuser:testpass --digest
|
||||
</command>
|
||||
</client>
|
||||
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol crlf="headers">
|
||||
GET /%TESTNUMBER HTTP/1.1
|
||||
GET /%TESTNUMBER" HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
GET /%TESTNUMBER HTTP/1.1
|
||||
GET /%TESTNUMBER" HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Authorization: Digest username="testuser", realm="testrealm", nonce="1053604145", uri="/%TESTNUMBER", response="c55f7f30d83d774a3d2dcacf725abaca"
|
||||
Authorization: Digest username="testuser", realm="testrealm", nonce="1053604145", uri="/%TESTNUMBER\"", response="1ee14b238b3259f17602e9ce41491ef9"
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue