digest: unquote realm and nonce before processing

RFC 7616 (and 2617) requires values to be "unquoted" before used for
digest calculations. The only place where unquoting can be done
correctly is header parsing function (realm="DOMAIN\\host" and
realm=DOMAN\\host are different realms).

This commit adds unquoting (de-escaping) of all values during header
parsing and quoting of the values during header forming. This approach
should be most straightforward and easy to read/maintain as all values
are processed in the same way as required by RFC.

Closes #8912
This commit is contained in:
Evgeny Grin 2022-05-25 10:20:18 +03:00 committed by Daniel Stenberg
parent f810047f9d
commit 3a6fe0c767
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
2 changed files with 36 additions and 15 deletions

View File

@ -81,12 +81,12 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) { for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
switch(*str) { switch(*str) {
case '\\': case '\\':
if(!escape) { if(starts_with_quote) {
/* possibly the start of an escaped quote */ if(!escape) {
escape = TRUE; /* the start of an escaped quote */
*content++ = '\\'; /* Even though this is an escape character, we still escape = TRUE;
store it as-is in the target buffer */ continue;
continue; }
} }
break; break;
@ -664,6 +664,8 @@ static CURLcode auth_create_digest_http_message(
char *cnonce = NULL; char *cnonce = NULL;
size_t cnonce_sz = 0; size_t cnonce_sz = 0;
char *userp_quoted; char *userp_quoted;
char *realm_quoted;
char *nonce_quoted;
char *response = NULL; char *response = NULL;
char *hashthis = NULL; char *hashthis = NULL;
char *tmp = NULL; char *tmp = NULL;
@ -786,16 +788,27 @@ static CURLcode auth_create_digest_http_message(
nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
Digest parameters are all quoted strings. Username which is provided by Digest parameters are all quoted strings. Username which is provided by
the user will need double quotes and backslashes within it escaped. For the user will need double quotes and backslashes within it escaped.
the other fields, this shouldn't be an issue. realm, nonce, and opaque realm, nonce, and opaque will need backslashes as well as they were
are copied as is from the server, escapes and all. cnonce is generated de-escaped when copied from request header. cnonce is generated with
with web-safe characters. uri is already percent encoded. nc is 8 hex web-safe characters. uri is already percent encoded. nc is 8 hex
characters. algorithm and qop with standard values only contain web-safe characters. algorithm and qop with standard values only contain web-safe
characters. characters.
*/ */
userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
if(!userp_quoted) if(!userp_quoted)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
realm_quoted = auth_digest_string_quoted(digest->realm);
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) { if(digest->qop) {
response = aprintf("username=\"%s\", " response = aprintf("username=\"%s\", "
@ -807,8 +820,8 @@ static CURLcode auth_create_digest_http_message(
"qop=%s, " "qop=%s, "
"response=\"%s\"", "response=\"%s\"",
userp_quoted, userp_quoted,
digest->realm, realm_quoted,
digest->nonce, nonce_quoted,
uripath, uripath,
digest->cnonce, digest->cnonce,
digest->nc, digest->nc,
@ -827,18 +840,26 @@ static CURLcode auth_create_digest_http_message(
"uri=\"%s\", " "uri=\"%s\", "
"response=\"%s\"", "response=\"%s\"",
userp_quoted, userp_quoted,
digest->realm, realm_quoted,
digest->nonce, nonce_quoted,
uripath, uripath,
request_digest); request_digest);
} }
free(nonce_quoted);
free(realm_quoted);
free(userp_quoted); free(userp_quoted);
if(!response) if(!response)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
/* Add the optional fields */ /* Add the optional fields */
if(digest->opaque) { if(digest->opaque) {
char *opaque_quoted;
/* Append the opaque */ /* 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, digest->opaque); tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
free(response); free(response);
if(!tmp) if(!tmp)

View File

@ -73,7 +73,7 @@ Accept: */*
GET /%TESTNUMBER HTTP/1.1 GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT Host: %HOSTIP:%HTTPPORT
Authorization: Digest username="testuser", realm="test \"this\" realm!!", nonce="1053604145", uri="/%TESTNUMBER", response="a1c7931ece9e8617bae2715045e4f49f" Authorization: Digest username="testuser", realm="test \"this\" realm!!", nonce="1053604145", uri="/%TESTNUMBER", response="df3246f44d2bc8de0e9f8fc4d7cf6e95"
User-Agent: curl/%VERSION User-Agent: curl/%VERSION
Accept: */* Accept: */*