]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Fix issue with HTTP Digest authentication, add unit tests (rdar://41709086)
authorMichael R Sweet <michael.r.sweet@gmail.com>
Wed, 29 Aug 2018 02:50:20 +0000 (22:50 -0400)
committerMichael R Sweet <michael.r.sweet@gmail.com>
Wed, 29 Aug 2018 02:50:20 +0000 (22:50 -0400)
CHANGES.md
cups/auth.c
cups/http-private.h
cups/http-support.c
cups/ipp.c
cups/testhttp.c

index 38293df3d3e3f28f350ef98b58029e142b63fa29..9df73f0a8514d10ca07e4377de6a75c6aa8394e6 100644 (file)
@@ -1,4 +1,4 @@
-CHANGES - 2.2.9 - 2018-08-27
+CHANGES - 2.2.9 - 2018-08-28
 ============================
 
 
@@ -28,6 +28,7 @@ Changes in CUPS v2.2.9
 - The scheduler did not validate that required initial request attributes were
   in the operation group (rdar://41098178)
 - Authentication in the web interface did not work on macOS (rdar://41444473)
+- Fixed an issue with HTTP Digest authentication (rdar://41709086)
 - The scheduler could crash when job history was purged (rdar://42198057)
 - Dropped non-working RSS subscriptions UI from web interface templates.
 - Fixed a memory leak for some IPP (extension) syntaxes.
index 175c53892006f8b33a6b132b2f09f479b84d9edb..72f524385b851eec85a7f3fb40319d0f4247527f 100644 (file)
@@ -272,14 +272,7 @@ cupsDoAuthentication(
                                        /* Opaque data from server */
                        cnonce[65],     /* cnonce value */
                        kd[65],         /* Final MD5/SHA-256 digest */
-                       ha1[65],        /* Hash of username:realm:password */
-                       ha2[65],        /* Hash of method:request-uri */
-                       hdata[65],      /* Hash of auth data */
-                       temp[1024],     /* Temporary string */
                        digest[1024];   /* Digest auth data */
-      unsigned char    hash[32];       /* Hash buffer */
-      const char       *hashalg;       /* Hashing algorithm */
-      size_t           hashsize;       /* Size of hash */
 
       if (strcmp(nonce, http->nonce))
       {
@@ -300,59 +293,12 @@ cupsDoAuthentication(
       if (cups_auth_param(schemedata, "algorithm", algorithm, sizeof(algorithm)))
       {
        /*
-        * Follow RFC 2617/7616...
+        * Calculate and pass the RFC 2617/7616 WWW-Authenticate header...
         */
 
-        if (!_cups_strcasecmp(algorithm, "MD5"))
-        {
-         /*
-          * RFC 2617 Digest with MD5
-          */
+        if (!_httpDigest(kd, sizeof(kd), algorithm, cupsUser(), realm, strchr(http->userpass, ':') + 1, nonce, http->nonce_count, cnonce, "auth", method, resource))
+          continue;
 
-          hashalg = "md5";
-       }
-       else if (!_cups_strcasecmp(algorithm, "SHA-256"))
-       {
-        /*
-         * RFC 7616 Digest with SHA-256
-         */
-
-          hashalg = "sha2-256";
-       }
-       else
-       {
-        /*
-         * Some other algorithm we don't support, skip this one...
-         */
-
-         continue;
-       }
-
-       /*
-        * Calculate digest value...
-        */
-
-        /* H(A1) = H(username:realm:password) */
-        snprintf(temp, sizeof(temp), "%s:%s:%s", cupsUser(), realm, strchr(http->userpass, ':') + 1);
-        hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, ha1, sizeof(ha1));
-
-        /* H(A2) = H(method:uri) */
-       snprintf(temp, sizeof(temp), "%s:%s", method, resource);
-        hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, ha2, sizeof(ha2));
-
-        /* H(data) = H(nonce:nc:cnonce:qop:H(A2)) */
-       snprintf(temp, sizeof(temp), "%s:%08x:%s:auth:%s", nonce, http->nonce_count, cnonce, ha2);
-        hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, hdata, sizeof(hdata));
-
-        /* KD = H(H(A1):H(data)) */
-       snprintf(temp, sizeof(temp), "%s:%s", ha1, hdata);
-        hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, kd, sizeof(kd));
-
-        /* Pass the RFC 2617/7616 WWW-Authenticate header */
         if (opaque[0])
          snprintf(digest, sizeof(digest), "username=\"%s\", realm=\"%s\", nonce=\"%s\", algorithm=%s, qop=auth, opaque=\"%s\", cnonce=\"%s\", nc=%08x, uri=\"%s\", response=\"%s\"", cupsUser(), realm, nonce, algorithm, opaque, cnonce, http->nonce_count, resource, kd);
        else
@@ -361,25 +307,12 @@ cupsDoAuthentication(
       else
       {
        /*
-        * Use old RFC 2069 Digest method...
+        * Calculate and pass the old RFC 2069 WWW-Authenticate header...
         */
 
-        /* H(A1) = H(username:realm:password) */
-        snprintf(temp, sizeof(temp), "%s:%s:%s", cupsUser(), realm, strchr(http->userpass, ':') + 1);
-        hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, ha1, sizeof(ha1));
-
-        /* H(A2) = H(method:uri) */
-       snprintf(temp, sizeof(temp), "%s:%s", method, resource);
-        hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, ha2, sizeof(ha2));
-
-        /* KD = H(H(A1):nonce:H(A2)) */
-       snprintf(temp, sizeof(temp), "%s:%s:%s", ha1, nonce, ha2);
-       hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
-       cupsHashString(hash, hashsize, kd, sizeof(kd));
+        if (!_httpDigest(kd, sizeof(kd), NULL, cupsUser(), realm, strchr(http->userpass, ':') + 1, nonce, http->nonce_count, NULL, NULL, method, resource))
+         continue;
 
-        /* Pass the RFC 2069 WWW-Authenticate header */
        snprintf(digest, sizeof(digest), "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", cupsUser(), realm, nonce, resource, kd);
       }
 
index 4f118bae57a95f12341a7d6aab8b042dd07a838d..84f487ff7ee6ba901f65309b7a88d7746fc5ecd1 100644 (file)
@@ -432,6 +432,7 @@ extern http_tls_credentials_t
                        _httpCreateCredentials(cups_array_t *credentials);
 extern char            *_httpDecodeURI(char *dst, const char *src,
                                        size_t dstsize);
+extern char            *_httpDigest(char *buffer, size_t bufsize, const char *algorithm, const char *username, const char *realm, const char *password, const char *nonce, unsigned nc, const char *cnonce, const char *qop, const char *method, const char *resource);
 extern void            _httpDisconnect(http_t *http);
 extern char            *_httpEncodeURI(char *dst, const char *src,
                                        size_t dstsize);
index 76c127222cec7fedd89662c60c16f10c5c3d967a..1c901689aa61069fec1e401fe65824b7e7407f93 100644 (file)
@@ -666,6 +666,111 @@ httpDecode64_2(char       *out,           /* I  - String to write to */
 }
 
 
+/*
+ * '_httpDigest()' - Calculate a Digest authentication response using the
+ *                   appropriate RFC 2068/2617/7616 algorithm.
+ */
+
+char *                                 /* O - Response string */
+_httpDigest(char       *buffer,                /* I - Response buffer */
+            size_t     bufsize,                /* I - Size of response buffer */
+            const char *algorithm,     /* I - algorithm value or `NULL` */
+            const char *username,      /* I - username value */
+            const char *realm,         /* I - realm value */
+            const char *password,      /* I - password value */
+            const char *nonce,         /* I - nonce value */
+            unsigned   nc,             /* I - nc value */
+            const char *cnonce,                /* I - cnonce value or `NULL` */
+            const char *qop,           /* I - qop value */
+            const char *method,                /* I - HTTP method */
+            const char *resource)      /* I - HTTP resource path */
+{
+  char         ha1[65],        /* Hash of username:realm:password */
+               ha2[65],        /* Hash of method:request-uri */
+               temp[1024];     /* Temporary string */
+  unsigned char        hash[32];       /* Hash buffer */
+  const char   *hashalg;       /* Hashing algorithm */
+  size_t       hashsize;       /* Size of hash */
+
+
+  if (algorithm)
+  {
+   /*
+    * Follow RFC 2617/7616...
+    */
+
+    if (!_cups_strcasecmp(algorithm, "MD5"))
+    {
+     /*
+      * RFC 2617 Digest with MD5
+      */
+
+      hashalg = "md5";
+    }
+    else if (!_cups_strcasecmp(algorithm, "SHA-256"))
+    {
+     /*
+      * RFC 7616 Digest with SHA-256
+      */
+
+      hashalg = "sha2-256";
+    }
+    else
+    {
+     /*
+      * Some other algorithm we don't support, skip this one...
+      */
+
+      *buffer = '\0';
+
+      return (NULL);
+    }
+
+   /*
+    * Calculate digest value...
+    */
+
+    /* H(A1) = H(username:realm:password) */
+    snprintf(temp, sizeof(temp), "%s:%s:%s", username, realm, password);
+    hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha1, sizeof(ha1));
+
+    /* H(A2) = H(method:uri) */
+    snprintf(temp, sizeof(temp), "%s:%s", method, resource);
+    hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha2, sizeof(ha2));
+
+    /* KD = H(H(A1):nonce:nc:cnonce:qop:H(A2)) */
+    snprintf(temp, sizeof(temp), "%s:%s:%08x:%s:%s:%s", ha1, nonce, nc, cnonce, qop, ha2);
+    hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, buffer, bufsize);
+  }
+  else
+  {
+   /*
+    * Use old RFC 2069 Digest method...
+    */
+
+    /* H(A1) = H(username:realm:password) */
+    snprintf(temp, sizeof(temp), "%s:%s:%s", username, realm, password);
+    hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha1, sizeof(ha1));
+
+    /* H(A2) = H(method:uri) */
+    snprintf(temp, sizeof(temp), "%s:%s", method, resource);
+    hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha2, sizeof(ha2));
+
+    /* KD = H(H(A1):nonce:H(A2)) */
+    snprintf(temp, sizeof(temp), "%s:%s:%s", ha1, nonce, ha2);
+    hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, buffer, bufsize);
+  }
+
+  return (buffer);
+}
+
+
 /*
  * 'httpEncode64()' - Base64-encode a string.
  *
index d6e39d5d899fa81d92585056b9def2b96270488c..652d59fd7906e56002aca3a78c000b05c63c4966 100644 (file)
@@ -4332,7 +4332,7 @@ ippSetString(ipp_t           *ipp,        /* I  - IPP message */
   if (!ipp || !attr || !*attr ||
       (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
        value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
-      !strvalue)
+      element < 0 || element > (*attr)->num_values || !strvalue)
     return (0);
 
  /*
index fa8356c02a451197d9ba9cb41a255b1a69dfb23c..64e0122742c07e815045c3d74490515eb1e05608 100644 (file)
@@ -341,6 +341,38 @@ main(int  argc,                            /* I - Number of command-line arguments */
     if (!j)
       puts("PASS");
 
+   /*
+    * _httpDigest()
+    */
+
+    fputs("_httpDigest(MD5): ", stdout);
+    if (!_httpDigest(buffer, sizeof(buffer), "MD5", "Mufasa", "http-auth@example.org", "Circle of Life", "7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", 1, "f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", "auth", "GET", "/dir/index.html"))
+    {
+      failures ++;
+      puts("FAIL (unable to calculate hash)");
+    }
+    else if (strcmp(buffer, "8ca523f5e9506fed4657c9700eebdbec"))
+    {
+      failures ++;
+      printf("FAIL (got \"%s\", expected \"8ca523f5e9506fed4657c9700eebdbec\")\n", buffer);
+    }
+    else
+      puts("PASS");
+
+    fputs("_httpDigest(SHA-256): ", stdout);
+    if (!_httpDigest(buffer, sizeof(buffer), "SHA-256", "Mufasa", "http-auth@example.org", "Circle of Life", "7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", 1, "f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", "auth", "GET", "/dir/index.html"))
+    {
+      failures ++;
+      puts("FAIL (unable to calculate hash)");
+    }
+    else if (strcmp(buffer, "753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1"))
+    {
+      failures ++;
+      printf("FAIL (got \"%s\", expected \"753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1\")\n", buffer);
+    }
+    else
+      puts("PASS");
+
    /*
     * httpGetHostname()
     */