]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
digest: unquote realm and nonce before processing
authorEvgeny Grin <k2k@narod.ru>
Wed, 25 May 2022 07:20:18 +0000 (10:20 +0300)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 2 Jun 2022 06:18:34 +0000 (08:18 +0200)
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

lib/vauth/digest.c
tests/data/test1095

index d4616095da4cad2e502165e4e5bd9be06523764a..ffc406035dda17a38b885eeade3e48d10ffc3816 100644 (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++) {
     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;
 
@@ -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;
@@ -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\", "
@@ -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,
@@ -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)
index efb8413b267da2c759a2c0d8bb7ed4d0aefb1c51..d4cc082d10c0f54f91fb51709c81fe7b9110d792 100644 (file)
@@ -73,7 +73,7 @@ Accept: */*
 \r
 GET /%TESTNUMBER HTTP/1.1\r
 Host: %HOSTIP:%HTTPPORT\r
-Authorization: Digest username="testuser", realm="test \"this\" realm!!", nonce="1053604145", uri="/%TESTNUMBER", response="a1c7931ece9e8617bae2715045e4f49f"\r
+Authorization: Digest username="testuser", realm="test \"this\" realm!!", nonce="1053604145", uri="/%TESTNUMBER", response="df3246f44d2bc8de0e9f8fc4d7cf6e95"\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
 \r