aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/digestauth.c
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-05-02 17:06:34 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-05-04 15:58:47 +0300
commitf2aa7b8853b03cf0e74afb38d1a8a5b7011de9db (patch)
treee75625f993014ae6850d665a2001c49a6a2d311b /src/microhttpd/digestauth.c
parent46554d2c55a9bacad6881fbec705d0ca7d962684 (diff)
downloadlibmicrohttpd-f2aa7b8853b03cf0e74afb38d1a8a5b7011de9db.tar.gz
libmicrohttpd-f2aa7b8853b03cf0e74afb38d1a8a5b7011de9db.zip
digestauth: added management of nonce-nc map array slots
Diffstat (limited to 'src/microhttpd/digestauth.c')
-rw-r--r--src/microhttpd/digestauth.c291
1 files changed, 239 insertions, 52 deletions
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 5fc4374b..bc47a2d3 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -41,6 +41,16 @@
41#include <windows.h> 41#include <windows.h>
42#endif /* MHD_W32_MUTEX_ */ 42#endif /* MHD_W32_MUTEX_ */
43 43
44
45/**
46 * Allow re-use of the nonce-nc map array slot after #REUSE_TIMEOUT seconds,
47 * if this slot is needed for the new nonce, while the old nonce was not used
48 * even one time by the client.
49 * Typically clients immediately use generated nonce for new request.
50 */
51#define REUSE_TIMEOUT 30
52
53
44/** 54/**
45 * 48 bit value in bytes 55 * 48 bit value in bytes
46 */ 56 */
@@ -564,42 +574,6 @@ get_nonce_nc_idx (size_t arr_size,
564 574
565 575
566/** 576/**
567 * Add the new nonce to the nonce-nc map array.
568 *
569 * @param connection The MHD connection structure
570 * @param nonce the pointer that referenced a zero-terminated array of nonce
571 * @param noncelen the lenth of @a nonce, in characters
572 * @return #MHD_YES if successful, #MHD_NO if invalid (or we have no NC array)
573 */
574static bool
575add_nonce (struct MHD_Connection *connection,
576 const char *nonce,
577 size_t noncelen)
578{
579 struct MHD_Daemon *const daemon = connection->daemon;
580 struct MHD_NonceNc *nn;
581
582 mhd_assert (MAX_NONCE_LENGTH >= noncelen);
583 if (0 == daemon->nonce_nc_size)
584 return false;
585
586 nn = &daemon->nnc[get_nonce_nc_idx (daemon->nonce_nc_size,
587 nonce,
588 noncelen)];
589
590 MHD_mutex_lock_chk_ (&daemon->nnc_lock);
591 memcpy (nn->nonce,
592 nonce,
593 noncelen);
594 nn->nonce[noncelen] = 0;
595 nn->nc = 0;
596 nn->nmask = 0;
597 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
598 return true;
599}
600
601
602/**
603 * Check nonce-nc map array with either new nonce counter 577 * Check nonce-nc map array with either new nonce counter
604 * or a whole new nonce. 578 * or a whole new nonce.
605 * 579 *
@@ -814,6 +788,219 @@ calculate_nonce (uint64_t nonce_time,
814 788
815 789
816/** 790/**
791 * Extract timestamp from the given nonce.
792 * @param nonce the nonce to check
793 * @param noncelen the lenght of the nonce, zero for autodetect
794 * @param[out] ptimestamp the pointer to store extracted timestamp
795 * @return true if timestamp was extracted,
796 * false if nonce does not have valid timestamp.
797 */
798static bool
799get_nonce_timestamp (const char *const nonce,
800 size_t noncelen,
801 uint64_t *const ptimestamp)
802{
803 mhd_assert ((0 == noncelen) || (strlen (nonce) == noncelen));
804 if (0 == noncelen)
805 noncelen = strlen (nonce);
806
807 if ( (NONCE_STD_LEN (SHA256_DIGEST_SIZE) != noncelen) &&
808 (NONCE_STD_LEN (MD5_DIGEST_SIZE) != noncelen) )
809 return false;
810
811 if (TIMESTAMP_CHARS_LEN !=
812 MHD_strx_to_uint64_n_ (nonce + noncelen - TIMESTAMP_CHARS_LEN,
813 TIMESTAMP_CHARS_LEN,
814 ptimestamp))
815 return false;
816 return true;
817}
818
819
820/**
821 * Check whether it is possible to use slot in nonce-nc map array.
822 *
823 * Should be called with mutex held to avoid external modification of
824 * the slot data.
825 *
826 * @param nn the pointer to the nonce-nc slot
827 * @param now the current time
828 * @param new_nonce the new nonce supposed to be stored in this slot,
829 * zero-terminated
830 * @param new_nonce_len the length of the @a new_nonce in chars, not including
831 * the terminating zero.
832 * @return true if the slot can be used to store the new nonce,
833 * false otherwise.
834 */
835static bool
836is_slot_available (const struct MHD_NonceNc *const nn,
837 const uint64_t now,
838 const char *const new_nonce,
839 size_t new_nonce_len)
840{
841 uint64_t timestamp;
842 bool timestamp_valid;
843 mhd_assert (new_nonce_len <= NONCE_STD_LEN (MAX_DIGEST));
844 mhd_assert (NONCE_STD_LEN (MAX_DIGEST) < MAX_NONCE_LENGTH);
845 if (0 == nn->nonce[0])
846 return true; /* The slot is empty */
847
848 if (0 != nn->nc)
849 return true; /* Client already used the nonce in this slot at least
850 one time, re-use the slot */
851
852 if (0 == memcmp (nn->nonce, new_nonce, new_nonce_len + 1))
853 {
854 /* The slot has the same nonce already, the same nonce was already generated
855 * and used, this slot cannot be used with the same nonce as it would
856 * just reset received 'nc' values. */
857 return false;
858 }
859
860 timestamp_valid = get_nonce_timestamp (nn->nonce, 0, &timestamp);
861 mhd_assert (timestamp_valid);
862 if (! timestamp_valid)
863 return true; /* Invalid timestamp in nonce-nc, should not be possible */
864
865 if ((REUSE_TIMEOUT * 1000) < TRIM_TO_TIMESTAMP (now - timestamp))
866 return true;
867
868 return false;
869}
870
871
872/**
873 * Calculate the server nonce so that it mitigates replay attacks and add
874 * the new nonce to the nonce-nc map array.
875 *
876 * @param connection the MHD connection structure
877 * @param timestamp the current timestamp
878 * @param realm the string of characters that describes the realm of auth
879 * @param da the digest algorithm to use
880 * @param[out] nonce the pointer to a character array for the nonce to put in,
881 * must provide NONCE_STD_LEN(da->digest_size)+1 bytes
882 * @return true if the new nonce has been added to the nonce-nc map array,
883 * false otherwise.
884 */
885static bool
886calculate_add_nonce (struct MHD_Connection *const connection,
887 uint64_t timestamp,
888 const char *realm,
889 struct DigestAlgorithm *da,
890 char *nonce)
891{
892 struct MHD_Daemon *const daemon = connection->daemon;
893 struct MHD_NonceNc *nn;
894 const size_t nonce_size = NONCE_STD_LEN (da->digest_size);
895 bool ret;
896
897 mhd_assert (MAX_NONCE_LENGTH >= nonce_size);
898 mhd_assert (0 != nonce_size);
899
900 calculate_nonce (timestamp,
901 connection->method,
902 connection->daemon->digest_auth_random,
903 connection->daemon->digest_auth_rand_size,
904 connection->url,
905 realm,
906 da,
907 nonce);
908
909 if (0 == daemon->nonce_nc_size)
910 return false;
911
912 nn = daemon->nnc + get_nonce_nc_idx (daemon->nonce_nc_size,
913 nonce,
914 nonce_size);
915
916 MHD_mutex_lock_chk_ (&daemon->nnc_lock);
917 if (is_slot_available (nn, timestamp, nonce, nonce_size))
918 {
919 memcpy (nn->nonce,
920 nonce,
921 nonce_size);
922 nn->nonce[nonce_size] = 0; /* With terminating zero */
923 nn->nc = 0;
924 nn->nmask = 0;
925 ret = true;
926 }
927 else
928 ret = false;
929 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
930
931 return ret;
932}
933
934
935/**
936 * Calculate the server nonce so that it mitigates replay attacks and add
937 * the new nonce to the nonce-nc map array.
938 *
939 * @param connection the MHD connection structure
940 * @param realm A string of characters that describes the realm of auth.
941 * @param da digest algorithm to use
942 * @param[out] nonce A pointer to a character array for the nonce to put in,
943 * must provide NONCE_STD_LEN(da->digest_size)+1 bytes
944 */
945static bool
946calculate_add_nonce_with_retry (struct MHD_Connection *const connection,
947 const char *realm,
948 struct DigestAlgorithm *da,
949 char *nonce)
950{
951 const uint64_t timestamp1 = MHD_monotonic_msec_counter ();
952
953 if (! calculate_add_nonce (connection, timestamp1, realm, da, nonce))
954 {
955 /* Either:
956 * 1. The same nonce was already generated. If it will be used then one
957 * of the clients will fail (as no initial 'nc' value could be given to
958 * the client, the second client which will use 'nc=00000001' will fail).
959 * 2. Another nonce uses the same slot, and this nonce never has been
960 * used by the client and this nonce is still fresh enough.
961 */
962 const size_t digest_size = da->digest_size;
963 char nonce2[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1];
964 uint64_t timestamp2;
965 if (0 == connection->daemon->nonce_nc_size)
966 return false; /* No need to re-try */
967
968 timestamp2 = MHD_monotonic_msec_counter ();
969 if (timestamp1 == timestamp2)
970 {
971 /* The timestamps are equal, need to generate some arbitrary
972 * difference for nonce. */
973 uint64_t base1;
974 uint32_t base2;
975 uint16_t base3;
976 uint8_t base4;
977 base1 = (uint64_t) (uintptr_t) nonce2;
978 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
979 base2 = _MHD_ROTL32 (base2, 4);
980 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
981 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
982 base1 = (uint64_t) (uintptr_t) connection;
983 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
984 base2 = _MHD_ROTL32 (base2, (((base1 >> 4) ^ base1) % 32));
985 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
986 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
987 timestamp2 -= (base4 & 0x7f); /* Use up to 127 ms difference */
988 }
989 if (! calculate_add_nonce (connection, timestamp2, realm, da, nonce2))
990 {
991 /* No free slot has been found. Re-tries are expensive, just use
992 * the generated nonce. As it is not stored in nonce-nc map array,
993 * the next request of the client will be recognized as valid, but 'stale'
994 * so client should re-try automatically. */
995 return false;
996 }
997 memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size) + 1);
998 }
999 return true;
1000}
1001
1002
1003/**
817 * Test if the given key-value pair is in the headers for the 1004 * Test if the given key-value pair is in the headers for the
818 * given connection. 1005 * given connection.
819 * 1006 *
@@ -1461,30 +1648,30 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
1461 if (NULL == response) 1648 if (NULL == response)
1462 return MHD_NO; 1649 return MHD_NO;
1463 1650
1651 if (0 == connection->daemon->nonce_nc_size)
1652 {
1653#ifdef HAVE_MESSAGES
1654 MHD_DLOG (connection->daemon,
1655 _ ("The nonce array size is zero.\n"));
1656#endif /* HAVE_MESSAGES */
1657 return MHD_NO;
1658 }
1659
1464 if (1) 1660 if (1)
1465 { 1661 {
1466 char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1]; 1662 char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
1467 1663
1468 VLA_CHECK_LEN_DIGEST (da.digest_size); 1664 VLA_CHECK_LEN_DIGEST (da.digest_size);
1469 /* Generating the server nonce */ 1665 if (! calculate_add_nonce_with_retry (connection, realm, &da, nonce))
1470 calculate_nonce (MHD_monotonic_msec_counter (),
1471 connection->method,
1472 connection->daemon->digest_auth_random,
1473 connection->daemon->digest_auth_rand_size,
1474 connection->url,
1475 realm,
1476 &da,
1477 nonce);
1478 if (! add_nonce (connection,
1479 nonce,
1480 NONCE_STD_LEN (da.digest_size)))
1481 { 1666 {
1482#ifdef HAVE_MESSAGES 1667#ifdef HAVE_MESSAGES
1483 MHD_DLOG (connection->daemon, 1668 MHD_DLOG (connection->daemon,
1484 _ ( 1669 _ ("Could not register nonce. Client's requests with this "
1485 "Could not register nonce (is the nonce array size zero?).\n")); 1670 "nonce will be always 'stale'. Probably clients' requests "
1486#endif 1671 "are too intensive.\n"));
1487 return MHD_NO; 1672#else /* ! HAVE_MESSAGES */
1673 (void) 0;
1674#endif /* ! HAVE_MESSAGES */
1488 } 1675 }
1489 /* Building the authentication header */ 1676 /* Building the authentication header */
1490 hlen = MHD_snprintf_ (NULL, 1677 hlen = MHD_snprintf_ (NULL,