-CHANGES - 2.2.9 - 2018-08-27
+CHANGES - 2.2.9 - 2018-08-28
============================
- 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.
/* 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))
{
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
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);
}
_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);
}
+/*
+ * '_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.
*
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);
/*
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()
*/