aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-07-18 20:53:41 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-07-19 17:50:06 +0300
commitda89942421e50ff63b2ece56acec8fc89521a80b (patch)
tree28ada5cce219a08bb9a0a79568e292b92f842b5e
parentcc40800c76a85e844493b4e2318ee88f6fc16422 (diff)
downloadlibmicrohttpd-da89942421e50ff63b2ece56acec8fc89521a80b.tar.gz
libmicrohttpd-da89942421e50ff63b2ece56acec8fc89521a80b.zip
digest_auth_check: mostly rewritten, simplified, optimised
Moved all simple checks (size comparison, then strings comparison) to the front, before starting any heavy digest calculcations. Used extracted parameters for calculations immediately, therefore avoided many memcpy()s and extra temporal storages.
-rw-r--r--src/microhttpd/digestauth.c592
1 files changed, 190 insertions, 402 deletions
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 73e20f29..d282aa62 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -242,7 +242,6 @@ struct DigestAlgorithm
242 /** 242 /**
243 * Buffer for hex-print of the final digest. 243 * Buffer for hex-print of the final digest.
244 */ 244 */
245 union DigestHex digest_hex;
246#if _DEBUG 245#if _DEBUG
247 bool setup; /**< The structure was set-up */ 246 bool setup; /**< The structure was set-up */
248 bool inited; /**< The calculation was initialised */ 247 bool inited; /**< The calculation was initialised */
@@ -378,6 +377,20 @@ digest_update (struct DigestAlgorithm *da,
378 377
379 378
380/** 379/**
380 * Feed digest calculation with more data from string.
381 * @param da the digest calculation
382 * @param str the zero-terminated string to process
383 */
384_MHD_static_inline void
385digest_update_str (struct DigestAlgorithm *da,
386 const char *str)
387{
388 const size_t str_len = strlen (str);
389 digest_update (da, (const uint8_t *) str, str_len);
390}
391
392
393/**
381 * Finally calculate hash (the digest). 394 * Finally calculate hash (the digest).
382 * @param da the digest calculation 395 * @param da the digest calculation
383 */ 396 */
@@ -413,244 +426,6 @@ digest_get_bin (struct DigestAlgorithm *da)
413} 426}
414 427
415 428
416/**
417 * Get pointer to the buffer for the printed digest.
418 * @param da the digest calculation
419 * @return the pointer to the buffer
420 */
421_MHD_static_inline char *
422digest_get_hex_buffer (struct DigestAlgorithm *da)
423{
424 return da->digest_hex.sha256;
425}
426
427
428/**
429 * Put calculated digest to the buffer as hex digits.
430 * @param da the digest calculation
431 * @return the pointer to the calculated digest
432 */
433_MHD_static_inline void
434digest_make_hex (struct DigestAlgorithm *da)
435{
436 MHD_bin_to_hex (digest_get_bin (da),
437 digest_get_size (da),
438 digest_get_hex_buffer (da));
439}
440
441
442/**
443 * calculate H(A1) from given hash as per RFC2617 spec
444 * and store the result in 'digest_hex'.
445 *
446 * @param alg The hash algorithm used, can be "MD5" or "MD5-sess"
447 * or "SHA-256" or "SHA-256-sess"
448 * Note that the rest of the code does not support the the "-sess" variants!
449 * @param[in,out] da digest implementation, must match @a alg; the
450 * da->digest_hex will be initialized to the digest in HEX
451 * @param digest An `unsigned char *' pointer to the binary MD5 sum
452 * for the precalculated hash value "username:realm:password"
453 * of #MHD_MD5_DIGEST_SIZE or #SHA256_DIGEST_SIZE bytes
454 * @param nonce A `char *' pointer to the nonce value
455 * @param cnonce A `char *' pointer to the cnonce value
456 */
457static void
458digest_calc_ha1_from_digest (const char *alg,
459 struct DigestAlgorithm *da,
460 const uint8_t *digest,
461 const char *nonce,
462 const char *cnonce)
463{
464 /* TODO: disable unsupported code paths */
465 if ( (MHD_str_equal_caseless_ (alg,
466 _MHD_MD5_TOKEN _MHD_SESS_TOKEN)) ||
467 (MHD_str_equal_caseless_ (alg,
468 _MHD_SHA256_TOKEN _MHD_SESS_TOKEN)) )
469 {
470 digest_init (da);
471 digest_update (da,
472 digest,
473 digest_get_size (da));
474 digest_update (da,
475 (const unsigned char *) ":",
476 1);
477 digest_update (da,
478 (const unsigned char *) nonce,
479 strlen (nonce));
480 digest_update (da,
481 (const unsigned char *) ":",
482 1);
483 digest_update (da,
484 (const unsigned char *) cnonce,
485 strlen (cnonce));
486 digest_calc_hash (da);
487 digest_make_hex (da);
488 }
489 else
490 {
491 MHD_bin_to_hex (digest,
492 digest_get_size (da),
493 digest_get_hex_buffer (da));
494 }
495}
496
497
498/**
499 * calculate H(A1) from username, realm and password as per RFC2617 spec
500 * and store the result in 'digest_hex'.
501 *
502 * @param alg The hash algorithm used, can be "MD5" or "MD5-sess"
503 * or "SHA-256" or "SHA-256-sess"
504 * @param username A `char *' pointer to the username value
505 * @param username the length of the @a username
506 * @param realm A `char *' pointer to the realm value
507 * @param realm_len the length of the @a realm
508 * @param password A `char *' pointer to the password value
509 * @param nonce A `char *' pointer to the nonce value
510 * @param cnonce A `char *' pointer to the cnonce value
511 * @param[in,out] da digest algorithm to use, and where to write
512 * the sessionkey to
513 */
514static void
515digest_calc_ha1_from_user (const char *alg,
516 const char *username,
517 size_t username_len,
518 const char *realm,
519 size_t realm_len,
520 const char *password,
521 const char *nonce,
522 const char *cnonce,
523 struct DigestAlgorithm *da)
524{
525 digest_init (da);
526 digest_update (da,
527 (const unsigned char *) username,
528 username_len);
529 digest_update (da,
530 (const unsigned char *) ":",
531 1);
532 digest_update (da,
533 (const unsigned char *) realm,
534 realm_len);
535 digest_update (da,
536 (const unsigned char *) ":",
537 1);
538 digest_update (da,
539 (const unsigned char *) password,
540 strlen (password));
541 digest_calc_hash (da);
542 digest_calc_ha1_from_digest (alg,
543 da,
544 digest_get_bin (da),
545 nonce,
546 cnonce);
547}
548
549
550/**
551 * Calculate request-digest/response-digest as per RFC2617 / RFC7616
552 * spec.
553 *
554 * @param ha1 H(A1), twice the @a da->digest_size + 1 bytes (0-terminated),
555 * MUST NOT be aliased with `da->sessionkey`!
556 * @param nonce nonce from server
557 * @param noncecount 8 hex digits
558 * @param cnonce client nonce
559 * @param qop qop-value: "", "auth" or "auth-int" (NOTE: only 'auth' is supported today.)
560 * @param method method from request
561 * @param uri requested URL, could be not zero-terminated
562 * @param uri_len the length of @a uri, in characters
563 * @param hentity H(entity body) if qop="auth-int"
564 * @param[in,out] da digest algorithm to use, also
565 * we write da->sessionkey (set to response request-digest or response-digest)
566 */
567static void
568digest_calc_response (const char *ha1,
569 const char *nonce,
570 const char *noncecount,
571 const char *cnonce,
572 const char *qop,
573 const char *method,
574 const char *uri,
575 size_t uri_len,
576 const char *hentity,
577 struct DigestAlgorithm *da)
578{
579 (void) hentity; /* Unused. Silence compiler warning. */
580
581 /* Calculate h(a2) */
582 digest_init (da);
583 digest_update (da,
584 (const unsigned char *) method,
585 strlen (method));
586 digest_update (da,
587 (const unsigned char *) ":",
588 1);
589 digest_update (da,
590 (const unsigned char *) uri,
591 uri_len);
592#if 0
593 if (0 == strcasecmp (qop,
594 "auth-int"))
595 {
596 /* This is dead code since the rest of this module does
597 not support auth-int. */
598 digest_update (da,
599 ":",
600 1);
601 if (NULL != hentity)
602 da->update (da->ctx,
603 hentity,
604 strlen (hentity));
605 }
606#endif
607 digest_calc_hash (da);
608 digest_make_hex (da);
609
610 /* calculate response */
611 digest_init (da);
612 digest_update (da,
613 (const unsigned char *) ha1,
614 digest_get_size (da) * 2);
615 digest_update (da,
616 (const unsigned char *) ":",
617 1);
618 digest_update (da,
619 (const unsigned char *) nonce,
620 strlen (nonce));
621 digest_update (da,
622 (const unsigned char *) ":",
623 1);
624 if ('\0' != *qop)
625 {
626 digest_update (da,
627 (const unsigned char *) noncecount,
628 strlen (noncecount));
629 digest_update (da,
630 (const unsigned char *) ":",
631 1);
632 digest_update (da,
633 (const unsigned char *) cnonce,
634 strlen (cnonce));
635 digest_update (da,
636 (const unsigned char *) ":",
637 1);
638 digest_update (da,
639 (const unsigned char *) qop,
640 strlen (qop));
641 digest_update (da,
642 (const unsigned char *) ":",
643 1);
644 }
645 digest_update (da,
646 (const unsigned char *) digest_get_hex_buffer (da),
647 digest_get_size (da) * 2);
648
649 digest_calc_hash (da);
650 digest_make_hex (da);
651}
652
653
654static const struct MHD_RqDAuth * 429static const struct MHD_RqDAuth *
655get_rq_dauth_params (struct MHD_Connection *connection) 430get_rq_dauth_params (struct MHD_Connection *connection)
656{ 431{
@@ -744,7 +519,8 @@ get_nonce_nc_idx (size_t arr_size,
744 * Check nonce-nc map array with the new nonce counter. 519 * Check nonce-nc map array with the new nonce counter.
745 * 520 *
746 * @param connection The MHD connection structure 521 * @param connection The MHD connection structure
747 * @param nonce A pointer that referenced a zero-terminated array of nonce 522 * @param nonce the pointer that referenced hex nonce, does not need to be
523 * zero-terminated
748 * @param noncelen the length of @a nonce, in characters 524 * @param noncelen the length of @a nonce, in characters
749 * @param nc The nonce counter 525 * @param nc The nonce counter
750 * @return #MHD_DAUTH_NONCENC_OK if successful, 526 * @return #MHD_DAUTH_NONCENC_OK if successful,
@@ -766,7 +542,6 @@ check_nonce_nc (struct MHD_Connection *connection,
766 enum MHD_CheckNonceNC_ ret; 542 enum MHD_CheckNonceNC_ ret;
767 543
768 mhd_assert (0 != noncelen); 544 mhd_assert (0 != noncelen);
769 mhd_assert (strlen (nonce) == noncelen);
770 mhd_assert (0 != nc); 545 mhd_assert (0 != nc);
771 if (MAX_DIGEST_NONCE_LENGTH < noncelen) 546 if (MAX_DIGEST_NONCE_LENGTH < noncelen)
772 return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis 547 return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis
@@ -800,7 +575,7 @@ check_nonce_nc (struct MHD_Connection *connection,
800 else 575 else
801 { 576 {
802 uint64_t slot_ts; /**< The timestamp in the slot */ 577 uint64_t slot_ts; /**< The timestamp in the slot */
803 if (! get_nonce_timestamp (nn->nonce, 0, &slot_ts)) 578 if (! get_nonce_timestamp (nn->nonce, noncelen, &slot_ts))
804 { 579 {
805 mhd_assert (0); /* The value is the slot is wrong */ 580 mhd_assert (0); /* The value is the slot is wrong */
806 ret = MHD_CHECK_NONCENC_STALE; 581 ret = MHD_CHECK_NONCENC_STALE;
@@ -2105,6 +1880,30 @@ is_param_equal (const struct MHD_RqDAuthParam *param,
2105 1880
2106 1881
2107/** 1882/**
1883 * Check whether Digest Auth request parameter is caseless equal to given string
1884 * @param param the parameter to check
1885 * @param str the string to compare with, does not need to be zero-terminated
1886 * @param str_len the length of the @a str
1887 * @return true is parameter is caseless equal to the given string,
1888 * false otherwise
1889 */
1890_MHD_static_inline bool
1891is_param_equal_caseless (const struct MHD_RqDAuthParam *param,
1892 const char *const str,
1893 const size_t str_len)
1894{
1895 mhd_assert (NULL != param->value.str);
1896 mhd_assert (0 != param->value.len);
1897 if (param->quoted)
1898 return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len,
1899 str, str_len);
1900 return (str_len == param->value.len) &&
1901 (0 == memcmp (str, param->value.str, str_len));
1902
1903}
1904
1905
1906/**
2108 * Authenticates the authorization header sent by the client 1907 * Authenticates the authorization header sent by the client
2109 * 1908 *
2110 * @param connection The MHD connection structure 1909 * @param connection The MHD connection structure
@@ -2136,13 +1935,16 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2136 char **pbuf) 1935 char **pbuf)
2137{ 1936{
2138 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); 1937 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
2139 char cnonce[MAX_CLIENT_NONCE_LENGTH]; 1938 static const uint8_t colon = (uint8_t) ':';
2140 const unsigned int digest_size = digest_get_size (da); 1939 const unsigned int digest_size = digest_get_size (da);
2141 char ha1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1]; 1940 char hdigest1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1];
2142 char qop[15]; /* auth,auth-int */ 1941 char hdigest2[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1];
2143 char nc[20]; 1942 char *ha1;
2144 char response[MAX_AUTH_RESPONSE_LENGTH]; 1943 char *ha2;
1944 uint8_t *response_bin;
1945#if 0
2145 const char *hentity = NULL; /* "auth-int" is not supported */ 1946 const char *hentity = NULL; /* "auth-int" is not supported */
1947#endif
2146 char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1]; 1948 char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1];
2147 uint64_t nonce_time; 1949 uint64_t nonce_time;
2148 uint64_t t; 1950 uint64_t t;
@@ -2154,17 +1956,18 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2154 struct _MHD_str_w_len unquoted; 1956 struct _MHD_str_w_len unquoted;
2155 struct _MHD_mstr_w_len unq_copy; 1957 struct _MHD_mstr_w_len unq_copy;
2156 enum _MHD_GetUnqResult unq_res; 1958 enum _MHD_GetUnqResult unq_res;
2157 enum MHD_DigestAuthResult ret;
2158 size_t username_len; 1959 size_t username_len;
2159 size_t realm_len; 1960 size_t realm_len;
2160 1961
2161 tmp2_size = 0; 1962 tmp2_size = 0;
1963 ha1 = NULL;
1964 ha2 = NULL;
2162 1965
2163 params = get_rq_dauth_params (connection); 1966 params = get_rq_dauth_params (connection);
2164 if (NULL == params) 1967 if (NULL == params)
2165 return MHD_DAUTH_WRONG_HEADER; 1968 return MHD_DAUTH_WRONG_HEADER;
2166 1969
2167 /* A quick check for presence of all required parameters */ 1970 /* ** A quick check for presence of all required parameters ** */
2168 if (NULL == params->username.value.str) 1971 if (NULL == params->username.value.str)
2169 return MHD_DAUTH_WRONG_HEADER; 1972 return MHD_DAUTH_WRONG_HEADER;
2170 1973
@@ -2178,10 +1981,19 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2178 else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len) 1981 else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len)
2179 return MHD_DAUTH_NONCE_WRONG; 1982 return MHD_DAUTH_NONCE_WRONG;
2180 1983
1984 if (NULL == params->nc.value.str)
1985 return MHD_DAUTH_WRONG_HEADER;
1986 else if (0 == params->nc.value.len)
1987 return MHD_DAUTH_WRONG_HEADER;
1988 else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
1989 return MHD_DAUTH_WRONG_HEADER;
1990
2181 if (NULL == params->cnonce.value.str) 1991 if (NULL == params->cnonce.value.str)
2182 return MHD_DAUTH_WRONG_HEADER; 1992 return MHD_DAUTH_WRONG_HEADER;
2183 else if (0 == params->cnonce.value.len) 1993 else if (0 == params->cnonce.value.len)
2184 return MHD_DAUTH_WRONG_HEADER; 1994 return MHD_DAUTH_WRONG_HEADER;
1995 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
1996 return MHD_DAUTH_TOO_LARGE;
2185 1997
2186 if (NULL == params->qop.value.str) 1998 if (NULL == params->qop.value.str)
2187 return MHD_DAUTH_WRONG_HEADER; 1999 return MHD_DAUTH_WRONG_HEADER;
@@ -2190,13 +2002,6 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2190 else if (MHD_STATICSTR_LEN_ ("auth-int") * 2 < params->qop.value.len) 2002 else if (MHD_STATICSTR_LEN_ ("auth-int") * 2 < params->qop.value.len)
2191 return MHD_DAUTH_WRONG_QOP; 2003 return MHD_DAUTH_WRONG_QOP;
2192 2004
2193 if (NULL == params->nc.value.str)
2194 return MHD_DAUTH_WRONG_HEADER;
2195 else if (0 == params->nc.value.len)
2196 return MHD_DAUTH_WRONG_HEADER;
2197 else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
2198 return MHD_DAUTH_WRONG_HEADER;
2199
2200 if (NULL == params->response.value.str) 2005 if (NULL == params->response.value.str)
2201 return MHD_DAUTH_WRONG_HEADER; 2006 return MHD_DAUTH_WRONG_HEADER;
2202 else if (0 == params->response.value.len) 2007 else if (0 == params->response.value.len)
@@ -2208,6 +2013,10 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2208 return MHD_DAUTH_WRONG_HEADER; 2013 return MHD_DAUTH_WRONG_HEADER;
2209 else if (0 == params->uri.value.len) 2014 else if (0 == params->uri.value.len)
2210 return MHD_DAUTH_WRONG_URI; 2015 return MHD_DAUTH_WRONG_URI;
2016 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
2017 return MHD_DAUTH_TOO_LARGE;
2018
2019 /* ** Check simple parameters match ** */
2211 2020
2212 /* Check 'username' */ 2021 /* Check 'username' */
2213 username_len = strlen (username); 2022 username_len = strlen (username);
@@ -2221,101 +2030,20 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2221 return MHD_DAUTH_WRONG_REALM; 2030 return MHD_DAUTH_WRONG_REALM;
2222 /* 'realm' valid */ 2031 /* 'realm' valid */
2223 2032
2224 /* Check 'nonce' */ 2033 /* Check 'qop' */
2225 unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size, 2034 /* TODO: support MHD_DIGEST_AUTH_QOP_NONE and MHD_DIGEST_AUTH_QOP_AUTH_INT */
2226 &unquoted); 2035 if (MHD_DIGEST_AUTH_QOP_AUTH != get_rq_qop (params))
2227 if (_MHD_UNQ_OK != unq_res)
2228 return MHD_DAUTH_ERROR;
2229 if (NONCE_STD_LEN (digest_size) != unquoted.len)
2230 return MHD_DAUTH_NONCE_WRONG;
2231
2232 if (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time))
2233 {
2234#ifdef HAVE_MESSAGES
2235 MHD_DLOG (daemon,
2236 _ ("Authentication failed, invalid timestamp format.\n"));
2237#endif
2238 return MHD_DAUTH_NONCE_WRONG;
2239 }
2240 t = MHD_monotonic_msec_counter ();
2241 /*
2242 * First level vetting for the nonce validity: if the timestamp
2243 * attached to the nonce exceeds `nonce_timeout', then the nonce is
2244 * invalid.
2245 */
2246 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
2247 return MHD_DAUTH_NONCE_STALE; /* too old */
2248
2249 calculate_nonce (nonce_time,
2250 connection->method,
2251 daemon->digest_auth_random,
2252 daemon->digest_auth_rand_size,
2253 connection->url,
2254 connection->url_len,
2255 connection->headers_received,
2256 realm,
2257 realm_len,
2258 da,
2259 noncehashexp);
2260 /*
2261 * Second level vetting for the nonce validity
2262 * if the timestamp attached to the nonce is valid
2263 * and possibly fabricated (in case of an attack)
2264 * the attacker must also know the random seed to be
2265 * able to generate a "sane" nonce, which if he does
2266 * not, the nonce fabrication process going to be
2267 * very hard to achieve.
2268 */
2269 if ((0 != strncmp (noncehashexp, unquoted.str, unquoted.len)) ||
2270 (0 != noncehashexp[unquoted.len]))
2271 return MHD_DAUTH_NONCE_WRONG;
2272 /* 'nonce' valid */
2273
2274 /* Get 'cnonce' */
2275 unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
2276 &unquoted);
2277 if (_MHD_UNQ_OK != unq_res)
2278 return (_MHD_UNQ_TOO_LARGE == unq_res) ?
2279 MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
2280
2281 if (sizeof(cnonce) <= unquoted.len)
2282 return MHD_DAUTH_ERROR; /* TODO: handle large client nonces */
2283
2284 /* TODO: avoid memcpy() */
2285 memcpy (cnonce, unquoted.str, unquoted.len);
2286 cnonce[unquoted.len] = 0;
2287 /* Got 'cnonce' */
2288
2289 /* Get 'qop' */
2290 unq_res = get_unquoted_param (&params->qop, tmp1, ptmp2, &tmp2_size,
2291 &unquoted);
2292 if (_MHD_UNQ_OK != unq_res)
2293 return MHD_DAUTH_ERROR;
2294
2295 if (sizeof(qop) <= unquoted.len)
2296 return MHD_DAUTH_ERROR; /* TODO: handle large client qop */
2297 /* TODO: avoid memcpy() */
2298 memcpy (qop, unquoted.str, unquoted.len);
2299 qop[unquoted.len] = 0;
2300 /* TODO: Really support empty value, support "auth-int" */
2301 if ( ((MHD_STATICSTR_LEN_ ("auth") != unquoted.len) ||
2302 (! MHD_str_equal_caseless_bin_n_ (qop, "auth", unquoted.len))) &&
2303 (0 != strcmp (qop,"")) )
2304 return MHD_DAUTH_WRONG_QOP; 2036 return MHD_DAUTH_WRONG_QOP;
2305 /* Got 'qop' */ 2037 /* 'qop' valid */
2306 2038
2307 /* Get 'nc' */ 2039 /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
2040 /* Get 'nc' digital value */
2308 unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size, 2041 unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
2309 &unquoted); 2042 &unquoted);
2310 if (_MHD_UNQ_OK != unq_res) 2043 if (_MHD_UNQ_OK != unq_res)
2311 return MHD_DAUTH_ERROR; 2044 return MHD_DAUTH_ERROR;
2312 2045
2313 if (sizeof(nc) <= unquoted.len) 2046 if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str,
2314 return MHD_DAUTH_ERROR;
2315 /* TODO: avoid memcpy() */
2316 memcpy (nc, unquoted.str, unquoted.len);
2317 nc[unquoted.len] = 0;
2318 if (unquoted.len != MHD_strx_to_uint64_n_ (nc,
2319 unquoted.len, 2047 unquoted.len,
2320 &nci)) 2048 &nci))
2321 { 2049 {
@@ -2333,25 +2061,30 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2333#endif 2061#endif
2334 return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ 2062 return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */
2335 } 2063 }
2336 /* Got 'nc' */ 2064 /* Got 'nc' digital value */
2337 2065 /* Get 'nonce' with basic checks */
2338 /* Get 'response' */ 2066 unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
2339 unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size,
2340 &unquoted); 2067 &unquoted);
2341 if (_MHD_UNQ_OK != unq_res) 2068 if (_MHD_UNQ_OK != unq_res)
2342 return MHD_DAUTH_ERROR; 2069 return MHD_DAUTH_ERROR;
2343 if (digest_size * 2 != unquoted.len)
2344 return MHD_DAUTH_RESPONSE_WRONG;
2345
2346 mhd_assert (sizeof(response) > unquoted.len);
2347
2348 if (sizeof(response) <= unquoted.len)
2349 return MHD_DAUTH_ERROR;
2350 /* TODO: avoid memcpy() */
2351 memcpy (response, unquoted.str, unquoted.len);
2352 response[unquoted.len] = 0;
2353 /* Got 'response' */
2354 2070
2071 if ((NONCE_STD_LEN (digest_size) != unquoted.len) ||
2072 (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)))
2073 {
2074#ifdef HAVE_MESSAGES
2075 MHD_DLOG (daemon,
2076 _ ("Authentication failed, invalid nonce format.\n"));
2077#endif
2078 return MHD_DAUTH_NONCE_WRONG;
2079 }
2080 t = MHD_monotonic_msec_counter ();
2081 /*
2082 * First level vetting for the nonce validity: if the timestamp
2083 * attached to the nonce exceeds `nonce_timeout', then the nonce is
2084 * invalid.
2085 */
2086 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
2087 return MHD_DAUTH_NONCE_STALE; /* too old */
2355 if (1) 2088 if (1)
2356 { 2089 {
2357 enum MHD_CheckNonceNC_ nonce_nc_check; 2090 enum MHD_CheckNonceNC_ nonce_nc_check;
@@ -2361,7 +2094,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2361 * generated previously. 2094 * generated previously.
2362 */ 2095 */
2363 nonce_nc_check = check_nonce_nc (connection, 2096 nonce_nc_check = check_nonce_nc (connection,
2364 noncehashexp, 2097 unquoted.str,
2365 NONCE_STD_LEN (digest_size), 2098 NONCE_STD_LEN (digest_size),
2366 nonce_time, 2099 nonce_time,
2367 nci); 2100 nci);
@@ -2385,60 +2118,115 @@ digest_auth_check_all_inner (struct MHD_Connection *connection,
2385 } 2118 }
2386 mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); 2119 mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check);
2387 } 2120 }
2121 /* The nonce was generated by MHD, is not stale and nonce-nc combination was
2122 not used before */
2388 2123
2124 /* ** Build H(A2) and check URI match in the header and in the request ** */
2389 /* Get 'uri' */ 2125 /* Get 'uri' */
2126 digest_init (da);
2127 digest_update_str (da, connection->method);
2128 digest_update (da, &colon, 1);
2129#if 0
2130 /* TODO: add support for "auth-int" */
2131 digest_update_str (da, hentity);
2132 digest_update (da, &colon, 1);
2133#endif
2390 unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size, 2134 unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size,
2391 &unq_copy); 2135 &unq_copy);
2392 if (_MHD_UNQ_OK != unq_res) 2136 if (_MHD_UNQ_OK != unq_res)
2393 return (_MHD_UNQ_TOO_LARGE == unq_res) ? 2137 return MHD_DAUTH_ERROR;
2394 MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
2395 2138
2396 if (NULL != digest) 2139 digest_update (da, (const uint8_t *) unq_copy.str, unq_copy.len);
2140 /* The next check will modify copied URI string */
2141 if (! check_uri_match (connection, unq_copy.str, unq_copy.len))
2142 return MHD_DAUTH_WRONG_URI;
2143 digest_calc_hash (da);
2144 ha2 = hdigest2;
2145 MHD_bin_to_hex (digest_get_bin (da), digest_size, ha2);
2146 /* Got H(A2) */
2147
2148 /* ** Build H(A1) ** */
2149 ha1 = hdigest1;
2150 if (NULL == digest)
2397 { 2151 {
2398 /* This will initialize da->digest_hex (ha1) */ 2152 digest_init (da);
2399 digest_calc_ha1_from_digest (digest_get_algo_name (da), 2153 digest_update (da, (const uint8_t *) username, username_len);
2400 da, 2154 digest_update (da, &colon, 1);
2401 digest, 2155 digest_update (da, (const uint8_t *) realm, realm_len);
2402 noncehashexp, 2156 digest_update (da, &colon, 1);
2403 cnonce); 2157 digest_update_str (da, password);
2158 digest_calc_hash (da);
2159 MHD_bin_to_hex (digest_get_bin (da), digest_size, ha1);
2404 } 2160 }
2405 else 2161 else
2406 { 2162 MHD_bin_to_hex (digest, digest_size, ha1);
2407 /* This will initialize da->digest_hex (ha1) */ 2163 /* TODO: support '-sess' versions */
2408 mhd_assert (NULL != password); /* NULL == digest => password != NULL */ 2164 /* Got H(A1) */
2409 digest_calc_ha1_from_user (digest_get_algo_name (da),
2410 username,
2411 username_len,
2412 realm,
2413 realm_len,
2414 password,
2415 noncehashexp,
2416 cnonce,
2417 da);
2418 }
2419 memcpy (ha1,
2420 digest_get_hex_buffer (da),
2421 digest_size * 2 + 1);
2422 /* This will initialize da->sessionkey (respexp) */
2423 digest_calc_response (ha1,
2424 noncehashexp,
2425 nc,
2426 cnonce,
2427 qop,
2428 connection->method,
2429 unq_copy.str,
2430 unq_copy.len,
2431 hentity,
2432 da);
2433 2165
2434 if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) 2166 /* ** Check 'response' ** */
2435 return MHD_DAUTH_WRONG_URI; 2167 digest_init (da);
2168 digest_update (da, (const uint8_t *) ha1, digest_size * 2);
2169 ha1 = NULL;
2170 /* H(A1) is not needed anymore, reuse the buffer */
2171 response_bin = (uint8_t *) hdigest1;
2172 unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size,
2173 &unquoted);
2174 if (_MHD_UNQ_OK != unq_res)
2175 return MHD_DAUTH_ERROR;
2176 if (digest_size != MHD_hex_to_bin (unquoted.str, unquoted.len, response_bin))
2177 return MHD_DAUTH_RESPONSE_WRONG;
2178 digest_update (da, &colon, 1);
2179 unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
2180 &unquoted);
2181 if (_MHD_UNQ_OK != unq_res)
2182 return MHD_DAUTH_ERROR;
2183 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
2184 digest_update (da, &colon, 1);
2185 unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
2186 &unquoted);
2187 if (_MHD_UNQ_OK != unq_res)
2188 return MHD_DAUTH_ERROR;
2189 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
2190 digest_update (da, &colon, 1);
2191 unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
2192 &unquoted);
2193 if (_MHD_UNQ_OK != unq_res)
2194 return MHD_DAUTH_ERROR;
2195 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
2196 digest_update (da, &colon, 1);
2197 unq_res = get_unquoted_param (&params->qop, tmp1, ptmp2, &tmp2_size,
2198 &unquoted);
2199 if (_MHD_UNQ_OK != unq_res)
2200 return MHD_DAUTH_ERROR;
2201 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
2202 digest_update (da, &colon, 1);
2203 digest_update (da, (const uint8_t *) ha2, digest_size * 2);
2204 ha2 = NULL;
2205 digest_calc_hash (da);
2206 if (0 != memcmp (digest_get_bin (da), response_bin, digest_size))
2207 return MHD_DAUTH_RESPONSE_WRONG;
2208 response_bin = NULL;
2436 2209
2437 ret = (0 == strcmp (response, 2210 /* It was already checked that 'nonce' (including timestamp) was generated
2438 digest_get_hex_buffer (da))) ? 2211 by MHD. The next check is mostly an overcaution. */
2439 MHD_DAUTH_OK : MHD_DAUTH_RESPONSE_WRONG; 2212 calculate_nonce (nonce_time,
2213 connection->method,
2214 daemon->digest_auth_random,
2215 daemon->digest_auth_rand_size,
2216 connection->url,
2217 connection->url_len,
2218 connection->headers_received,
2219 realm,
2220 realm_len,
2221 da,
2222 noncehashexp);
2440 2223
2441 return ret; 2224 if (! is_param_equal (&params->nonce, noncehashexp,
2225 NONCE_STD_LEN (digest_size)))
2226 return MHD_DAUTH_NONCE_WRONG;
2227 /* The 'nonce' was generated in the same conditions */
2228
2229 return MHD_DAUTH_OK;
2442} 2230}
2443 2231
2444 2232