Skip to content

Commit

Permalink
digest: unquote realm and nonce before processing
Browse files Browse the repository at this point in the history
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 curl#8912
  • Loading branch information
Karlson2k authored and bagder committed Jun 2, 2022
1 parent f810047 commit 3a6fe0c
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
49 changes: 35 additions & 14 deletions lib/vauth/digest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
switch(*str) {
case '\\':
if(!escape) {
/* possibly the start of an escaped quote */
escape = TRUE;
*content++ = '\\'; /* Even though this is an escape character, we still
store it as-is in the target buffer */
continue;
if(starts_with_quote) {
if(!escape) {
/* the start of an escaped quote */
escape = TRUE;
continue;
}
}
break;

Expand Down Expand Up @@ -664,6 +664,8 @@ static CURLcode auth_create_digest_http_message(
char *cnonce = NULL;
size_t cnonce_sz = 0;
char *userp_quoted;
char *realm_quoted;
char *nonce_quoted;
char *response = NULL;
char *hashthis = NULL;
char *tmp = NULL;
Expand Down Expand Up @@ -786,16 +788,27 @@ static CURLcode auth_create_digest_http_message(
nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
Digest parameters are all quoted strings. Username which is provided by
the user will need double quotes and backslashes within it escaped. For
the other fields, this shouldn't be an issue. realm, nonce, and opaque
are copied as is from the server, escapes and all. cnonce is generated
with web-safe characters. uri is already percent encoded. nc is 8 hex
the user will need double quotes and backslashes within it escaped.
realm, nonce, and opaque will need backslashes as well as they were
de-escaped when copied from request header. cnonce is generated with
web-safe characters. uri is already percent encoded. nc is 8 hex
characters. algorithm and qop with standard values only contain web-safe
characters.
*/
userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
if(!userp_quoted)
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) {
response = aprintf("username=\"%s\", "
Expand All @@ -807,8 +820,8 @@ static CURLcode auth_create_digest_http_message(
"qop=%s, "
"response=\"%s\"",
userp_quoted,
digest->realm,
digest->nonce,
realm_quoted,
nonce_quoted,
uripath,
digest->cnonce,
digest->nc,
Expand All @@ -827,18 +840,26 @@ static CURLcode auth_create_digest_http_message(
"uri=\"%s\", "
"response=\"%s\"",
userp_quoted,
digest->realm,
digest->nonce,
realm_quoted,
nonce_quoted,
uripath,
request_digest);
}
free(nonce_quoted);
free(realm_quoted);
free(userp_quoted);
if(!response)
return CURLE_OUT_OF_MEMORY;

/* Add the optional fields */
if(digest->opaque) {
char *opaque_quoted;
/* 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);
free(response);
if(!tmp)
Expand Down
2 changes: 1 addition & 1 deletion tests/data/test1095
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Accept: */*

GET /%TESTNUMBER HTTP/1.1
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
Accept: */*

Expand Down

0 comments on commit 3a6fe0c

Please sign in to comment.