[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libmicrohttpd] 11/12: digestauth: added management of nonce-nc map arra
From: |
gnunet |
Subject: |
[libmicrohttpd] 11/12: digestauth: added management of nonce-nc map array slots |
Date: |
Wed, 04 May 2022 14:59:45 +0200 |
This is an automated email from the git hooks/post-receive script.
karlson2k pushed a commit to branch master
in repository libmicrohttpd.
commit f2aa7b8853b03cf0e74afb38d1a8a5b7011de9db
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Mon May 2 17:06:34 2022 +0300
digestauth: added management of nonce-nc map array slots
---
src/microhttpd/digestauth.c | 291 ++++++++++++++++++++++++++++++++++++--------
1 file 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 @@
#include <windows.h>
#endif /* MHD_W32_MUTEX_ */
+
+/**
+ * Allow re-use of the nonce-nc map array slot after #REUSE_TIMEOUT seconds,
+ * if this slot is needed for the new nonce, while the old nonce was not used
+ * even one time by the client.
+ * Typically clients immediately use generated nonce for new request.
+ */
+#define REUSE_TIMEOUT 30
+
+
/**
* 48 bit value in bytes
*/
@@ -563,42 +573,6 @@ get_nonce_nc_idx (size_t arr_size,
}
-/**
- * Add the new nonce to the nonce-nc map array.
- *
- * @param connection The MHD connection structure
- * @param nonce the pointer that referenced a zero-terminated array of nonce
- * @param noncelen the lenth of @a nonce, in characters
- * @return #MHD_YES if successful, #MHD_NO if invalid (or we have no NC array)
- */
-static bool
-add_nonce (struct MHD_Connection *connection,
- const char *nonce,
- size_t noncelen)
-{
- struct MHD_Daemon *const daemon = connection->daemon;
- struct MHD_NonceNc *nn;
-
- mhd_assert (MAX_NONCE_LENGTH >= noncelen);
- if (0 == daemon->nonce_nc_size)
- return false;
-
- nn = &daemon->nnc[get_nonce_nc_idx (daemon->nonce_nc_size,
- nonce,
- noncelen)];
-
- MHD_mutex_lock_chk_ (&daemon->nnc_lock);
- memcpy (nn->nonce,
- nonce,
- noncelen);
- nn->nonce[noncelen] = 0;
- nn->nc = 0;
- nn->nmask = 0;
- MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
- return true;
-}
-
-
/**
* Check nonce-nc map array with either new nonce counter
* or a whole new nonce.
@@ -813,6 +787,219 @@ calculate_nonce (uint64_t nonce_time,
}
+/**
+ * Extract timestamp from the given nonce.
+ * @param nonce the nonce to check
+ * @param noncelen the lenght of the nonce, zero for autodetect
+ * @param[out] ptimestamp the pointer to store extracted timestamp
+ * @return true if timestamp was extracted,
+ * false if nonce does not have valid timestamp.
+ */
+static bool
+get_nonce_timestamp (const char *const nonce,
+ size_t noncelen,
+ uint64_t *const ptimestamp)
+{
+ mhd_assert ((0 == noncelen) || (strlen (nonce) == noncelen));
+ if (0 == noncelen)
+ noncelen = strlen (nonce);
+
+ if ( (NONCE_STD_LEN (SHA256_DIGEST_SIZE) != noncelen) &&
+ (NONCE_STD_LEN (MD5_DIGEST_SIZE) != noncelen) )
+ return false;
+
+ if (TIMESTAMP_CHARS_LEN !=
+ MHD_strx_to_uint64_n_ (nonce + noncelen - TIMESTAMP_CHARS_LEN,
+ TIMESTAMP_CHARS_LEN,
+ ptimestamp))
+ return false;
+ return true;
+}
+
+
+/**
+ * Check whether it is possible to use slot in nonce-nc map array.
+ *
+ * Should be called with mutex held to avoid external modification of
+ * the slot data.
+ *
+ * @param nn the pointer to the nonce-nc slot
+ * @param now the current time
+ * @param new_nonce the new nonce supposed to be stored in this slot,
+ * zero-terminated
+ * @param new_nonce_len the length of the @a new_nonce in chars, not including
+ * the terminating zero.
+ * @return true if the slot can be used to store the new nonce,
+ * false otherwise.
+ */
+static bool
+is_slot_available (const struct MHD_NonceNc *const nn,
+ const uint64_t now,
+ const char *const new_nonce,
+ size_t new_nonce_len)
+{
+ uint64_t timestamp;
+ bool timestamp_valid;
+ mhd_assert (new_nonce_len <= NONCE_STD_LEN (MAX_DIGEST));
+ mhd_assert (NONCE_STD_LEN (MAX_DIGEST) < MAX_NONCE_LENGTH);
+ if (0 == nn->nonce[0])
+ return true; /* The slot is empty */
+
+ if (0 != nn->nc)
+ return true; /* Client already used the nonce in this slot at least
+ one time, re-use the slot */
+
+ if (0 == memcmp (nn->nonce, new_nonce, new_nonce_len + 1))
+ {
+ /* The slot has the same nonce already, the same nonce was already
generated
+ * and used, this slot cannot be used with the same nonce as it would
+ * just reset received 'nc' values. */
+ return false;
+ }
+
+ timestamp_valid = get_nonce_timestamp (nn->nonce, 0, ×tamp);
+ mhd_assert (timestamp_valid);
+ if (! timestamp_valid)
+ return true; /* Invalid timestamp in nonce-nc, should not be possible */
+
+ if ((REUSE_TIMEOUT * 1000) < TRIM_TO_TIMESTAMP (now - timestamp))
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Calculate the server nonce so that it mitigates replay attacks and add
+ * the new nonce to the nonce-nc map array.
+ *
+ * @param connection the MHD connection structure
+ * @param timestamp the current timestamp
+ * @param realm the string of characters that describes the realm of auth
+ * @param da the digest algorithm to use
+ * @param[out] nonce the pointer to a character array for the nonce to put in,
+ * must provide NONCE_STD_LEN(da->digest_size)+1 bytes
+ * @return true if the new nonce has been added to the nonce-nc map array,
+ * false otherwise.
+ */
+static bool
+calculate_add_nonce (struct MHD_Connection *const connection,
+ uint64_t timestamp,
+ const char *realm,
+ struct DigestAlgorithm *da,
+ char *nonce)
+{
+ struct MHD_Daemon *const daemon = connection->daemon;
+ struct MHD_NonceNc *nn;
+ const size_t nonce_size = NONCE_STD_LEN (da->digest_size);
+ bool ret;
+
+ mhd_assert (MAX_NONCE_LENGTH >= nonce_size);
+ mhd_assert (0 != nonce_size);
+
+ calculate_nonce (timestamp,
+ connection->method,
+ connection->daemon->digest_auth_random,
+ connection->daemon->digest_auth_rand_size,
+ connection->url,
+ realm,
+ da,
+ nonce);
+
+ if (0 == daemon->nonce_nc_size)
+ return false;
+
+ nn = daemon->nnc + get_nonce_nc_idx (daemon->nonce_nc_size,
+ nonce,
+ nonce_size);
+
+ MHD_mutex_lock_chk_ (&daemon->nnc_lock);
+ if (is_slot_available (nn, timestamp, nonce, nonce_size))
+ {
+ memcpy (nn->nonce,
+ nonce,
+ nonce_size);
+ nn->nonce[nonce_size] = 0; /* With terminating zero */
+ nn->nc = 0;
+ nn->nmask = 0;
+ ret = true;
+ }
+ else
+ ret = false;
+ MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
+
+ return ret;
+}
+
+
+/**
+ * Calculate the server nonce so that it mitigates replay attacks and add
+ * the new nonce to the nonce-nc map array.
+ *
+ * @param connection the MHD connection structure
+ * @param realm A string of characters that describes the realm of auth.
+ * @param da digest algorithm to use
+ * @param[out] nonce A pointer to a character array for the nonce to put in,
+ * must provide NONCE_STD_LEN(da->digest_size)+1 bytes
+ */
+static bool
+calculate_add_nonce_with_retry (struct MHD_Connection *const connection,
+ const char *realm,
+ struct DigestAlgorithm *da,
+ char *nonce)
+{
+ const uint64_t timestamp1 = MHD_monotonic_msec_counter ();
+
+ if (! calculate_add_nonce (connection, timestamp1, realm, da, nonce))
+ {
+ /* Either:
+ * 1. The same nonce was already generated. If it will be used then one
+ * of the clients will fail (as no initial 'nc' value could be given to
+ * the client, the second client which will use 'nc=00000001' will fail).
+ * 2. Another nonce uses the same slot, and this nonce never has been
+ * used by the client and this nonce is still fresh enough.
+ */
+ const size_t digest_size = da->digest_size;
+ char nonce2[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1];
+ uint64_t timestamp2;
+ if (0 == connection->daemon->nonce_nc_size)
+ return false; /* No need to re-try */
+
+ timestamp2 = MHD_monotonic_msec_counter ();
+ if (timestamp1 == timestamp2)
+ {
+ /* The timestamps are equal, need to generate some arbitrary
+ * difference for nonce. */
+ uint64_t base1;
+ uint32_t base2;
+ uint16_t base3;
+ uint8_t base4;
+ base1 = (uint64_t) (uintptr_t) nonce2;
+ base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
+ base2 = _MHD_ROTL32 (base2, 4);
+ base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
+ base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
+ base1 = (uint64_t) (uintptr_t) connection;
+ base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
+ base2 = _MHD_ROTL32 (base2, (((base1 >> 4) ^ base1) % 32));
+ base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
+ base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
+ timestamp2 -= (base4 & 0x7f); /* Use up to 127 ms difference */
+ }
+ if (! calculate_add_nonce (connection, timestamp2, realm, da, nonce2))
+ {
+ /* No free slot has been found. Re-tries are expensive, just use
+ * the generated nonce. As it is not stored in nonce-nc map array,
+ * the next request of the client will be recognized as valid, but
'stale'
+ * so client should re-try automatically. */
+ return false;
+ }
+ memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size) + 1);
+ }
+ return true;
+}
+
+
/**
* Test if the given key-value pair is in the headers for the
* given connection.
@@ -1461,30 +1648,30 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection
*connection,
if (NULL == response)
return MHD_NO;
+ if (0 == connection->daemon->nonce_nc_size)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("The nonce array size is zero.\n"));
+#endif /* HAVE_MESSAGES */
+ return MHD_NO;
+ }
+
if (1)
{
char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
VLA_CHECK_LEN_DIGEST (da.digest_size);
- /* Generating the server nonce */
- calculate_nonce (MHD_monotonic_msec_counter (),
- connection->method,
- connection->daemon->digest_auth_random,
- connection->daemon->digest_auth_rand_size,
- connection->url,
- realm,
- &da,
- nonce);
- if (! add_nonce (connection,
- nonce,
- NONCE_STD_LEN (da.digest_size)))
+ if (! calculate_add_nonce_with_retry (connection, realm, &da, nonce))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
- _ (
- "Could not register nonce (is the nonce array size
zero?).\n"));
-#endif
- return MHD_NO;
+ _ ("Could not register nonce. Client's requests with this "
+ "nonce will be always 'stale'. Probably clients' requests "
+ "are too intensive.\n"));
+#else /* ! HAVE_MESSAGES */
+ (void) 0;
+#endif /* ! HAVE_MESSAGES */
}
/* Building the authentication header */
hlen = MHD_snprintf_ (NULL,
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [libmicrohttpd] branch master updated (76b68f65 -> d031ea8c), gnunet, 2022/05/04
- [libmicrohttpd] 04/12: digestauth: changed type of var, gnunet, 2022/05/04
- [libmicrohttpd] 01/12: internal.h: fixed doxy, gnunet, 2022/05/04
- [libmicrohttpd] 02/12: .gitignore: Updated, gnunet, 2022/05/04
- [libmicrohttpd] 05/12: digestauth: additional assert, gnunet, 2022/05/04
- [libmicrohttpd] 08/12: digestauth: use mseconds for timestamp, gnunet, 2022/05/04
- [libmicrohttpd] 09/12: digestauth: additional macros for readability, gnunet, 2022/05/04
- [libmicrohttpd] 07/12: digestauth: increased timestamp to 48 bits, gnunet, 2022/05/04
- [libmicrohttpd] 06/12: digestauth: added run-time checks for algo value, gnunet, 2022/05/04
- [libmicrohttpd] 11/12: digestauth: added management of nonce-nc map array slots,
gnunet <=
- [libmicrohttpd] 03/12: digestauth: use 7 bit shift for fast "hash", gnunet, 2022/05/04
- [libmicrohttpd] 10/12: digestauth: additional static function for code re-use, gnunet, 2022/05/04
- [libmicrohttpd] 12/12: digestauth: re-used static function, gnunet, 2022/05/04