gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] 16/19: Added MHD_queue_auth_required_response3(); Refact


From: gnunet
Subject: [libmicrohttpd] 16/19: Added MHD_queue_auth_required_response3(); Refactored public Digest Auth API v3
Date: Thu, 28 Jul 2022 06:26:20 +0200

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit c03c57c9d2d95bd739ab8a149597658d1ec95478
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Fri Jul 22 10:31:04 2022 +0300

    Added MHD_queue_auth_required_response3(); Refactored public Digest Auth 
API v3
    
    Added brand new function with more complete support for RFC 7616
    features. New function implemented from scratch. Old functions became
    wrappers for the new function, so fixes are inherited.
    
    Fixes:
    * All user values are properly quoted
    Features:
    * Added support for optional 'domain' Digest auth parameter
    * Realm now optional
    * Added userhash support
    * Added charset support
    
    For other Digest Auth v3 functions:
    * Added more parameters for complete control of Auth process from
      application side.
---
 src/examples/digest_auth_example.c        |   4 +-
 src/include/microhttpd.h                  | 171 +++++--
 src/microhttpd/digestauth.c               | 816 +++++++++++++++++++++---------
 src/testcurl/test_digestauth_concurrent.c |   5 +-
 src/testcurl/test_digestauth_emu_ext.c    |   4 +-
 5 files changed, 734 insertions(+), 266 deletions(-)

diff --git a/src/examples/digest_auth_example.c 
b/src/examples/digest_auth_example.c
index 4f576bf0..db8a1f28 100644
--- a/src/examples/digest_auth_example.c
+++ b/src/examples/digest_auth_example.c
@@ -84,8 +84,8 @@ ahc_echo (void *cls,
   res_e = MHD_digest_auth_check3 (connection, realm,
                                   username,
                                   password,
-                                  300,
-                                  MHD_DIGEST_ALG_MD5);
+                                  300, 60, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                  MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
   MHD_free (username);
   if (res_e != MHD_DAUTH_OK)
   {
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 9cf44ccb..aceed304 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097525
+#define MHD_VERSION 0x00097526
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -329,12 +329,6 @@ _MHD_DEPR_MACRO ( \
 #endif
 
 
-/**
- * Length of the binary output of the MD5 hash function.
- */
-#define  MHD_MD5_DIGEST_SIZE 16
-
-
 /**
  * @defgroup httpcode HTTP response codes.
  * These are the status codes defined for HTTP responses.
@@ -4327,6 +4321,22 @@ MHD_destroy_post_processor (struct MHD_PostProcessor 
*pp);
 /* ********************* Digest Authentication functions *************** */
 
 
+/**
+ * Length of the binary output of the MD5 hash function.
+ * @sa #MHD_digest_get_hash_size()
+ * @ingroup authentication
+ */
+#define MHD_MD5_DIGEST_SIZE 16
+
+
+/**
+ * Length of the binary output of the SHA-256 hash function.
+ * @sa #MHD_digest_get_hash_size()
+ * @ingroup authentication
+ */
+#define MHD_SHA256_DIGEST_SIZE 32
+
+
 /**
  * Constant to indicate that the nonce of the provided
  * authentication code was wrong.
@@ -4707,7 +4717,7 @@ struct MHD_DigestAuthInfo
    * long.
    * @warning This is binary data, no zero termination.
    * @warning To avoid buffer overruns, always check the size of the data 
before
-   *          use, because @ userhash_bin can point even to zero-sized
+   *          use, because @a userhash_bin can point even to zero-sized
    *          data.
    */
   uint8_t *userhash_bin;
@@ -4753,9 +4763,9 @@ struct MHD_DigestAuthInfo
 
   /**
    * The nc parameter value.
-   * Can be used by application to limit the number of nonce re-uses. If @ nc
-   * is higher than application wants to allow, then fail response with
-   * 'stale=true' could be used to ask force client to get the fresh 'nonce'.
+   * Can be used by application to limit the number of nonce re-uses. If @a nc
+   * is higher than application wants to allow, then auth required response 
with
+   * 'stale=true' could be used to force client to get the fresh 'nonce'.
    * If not specified by client or does not have hexadecimal digits only, the
    * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE.
    */
@@ -4819,12 +4829,29 @@ struct MHD_DigestAuthUsernameInfo
    * long.
    * @warning This is binary data, no zero termination.
    * @warning To avoid buffer overruns, always check the size of the data 
before
-   *          use, because @ userhash_bin can point even to zero-sized
+   *          use, because @a userhash_bin can point even to zero-sized
    *          data.
    */
   uint8_t *userhash_bin;
 };
 
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * The size of the digest specifies the size of the userhash, userdigest
+ * and other parameters which size depends on used hash algorithm.
+ * @param algo3 the algorithm to check
+ * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ *         #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not
+ *         recognised/valid
+ * @note Available since #MHD_VERSION 0x00097526
+ * @ingroup authentication
+ */
+_MHD_EXTERN size_t
+MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3);
+
+
 /**
  * Get the username from Digest Authorization client's header.
  *
@@ -4868,7 +4895,7 @@ enum MHD_DigestAuthAlgorithm
 {
 
   /**
-   * MHD should pick (currently defaults to SHA-256).
+   * MHD should pick (currently defaults to MD5).
    */
   MHD_DIGEST_ALG_AUTO = 0,
 
@@ -4969,10 +4996,17 @@ enum MHD_DigestAuthResult
  * @param username the username needs to be authenticated
  * @param password the password used in the authentication
  * @param nonce_timeout the nonce validity duration in seconds
- * @param algo the digest algorithms allowed for verification
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097521
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -4981,34 +5015,49 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
                         const char *username,
                         const char *password,
                         unsigned int nonce_timeout,
-                        enum MHD_DigestAuthAlgorithm algo);
+                        uint32_t max_nc,
+                        enum MHD_DigestAuthMultiQOP mqop,
+                        enum MHD_DigestAuthMultiAlgo3 malgo3);
 
 
 /**
- * Authenticates the authorization header sent by the client.
+ * Authenticates the authorization header sent by the client by using
+ * hash of "username:realm:password".
  *
  * @param connection the MHD connection structure
- * @param realm the realm to be used for authorization of the client
+ * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
- * @param digest the pointer to the binary digest for the precalculated hash
- *        value "username:realm:password" with specified @a algo
- * @param digest_size the number of bytes in @a digest (the size must match
- *        @a algo!)
- * @param nonce_timeout the nonce validity duration in seconds
- * @param algo digest algorithms allowed for verification
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param userdigest_size the size of the @a userdigest in bytes, must match 
the
+ *                        hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
+ *                        #MHD_SHA256_DIGEST_SIZE)
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based
+ *               algorithms cannot be used at the same time for this function
+ *               as @a userdigest_size must match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097521
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
 MHD_digest_auth_check_digest3 (struct MHD_Connection *connection,
                                const char *realm,
                                const char *username,
-                               const uint8_t *digest,
-                               size_t digest_size,
+                               const void *userdigest,
+                               size_t userdigest_size,
                                unsigned int nonce_timeout,
-                               enum MHD_DigestAuthAlgorithm algo);
+                               uint32_t max_nc,
+                               enum MHD_DigestAuthMultiQOP mqop,
+                               enum MHD_DigestAuthMultiAlgo3 malgo3);
 
 
 /**
@@ -5121,6 +5170,66 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
 /**
  * Queues a response to request authentication from the client
  *
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed (by #MHD_destroy_response()) after call of
+ * this function.
+ *
+ * @param connection the MHD connection structure
+ * @param realm the realm presented to the client
+ * @param opaque the string for opaque value, can be NULL, but NULL is
+ *               not recommended for better compatibility with clients
+ * @param domain the optional space-separated list of URIs for which the
+ *               same authorisation could be used, URIs can be in form
+ *               "path-absolute" (the path for the same host with initial 
slash)
+ *               or in form "absolute-URI" (the full path with protocol), in
+ *               any case client may assume that any URI which starts with
+ *               any of specified URI is in the same "protection space";
+ *               could be NULL (clients typically assume that the same
+ *               credentials could be used for any URI on the same host)
+ * @param response the reply to send; should contain the "access denied"
+ *                 body; note that this function sets the "WWW Authenticate"
+ *                 header and that the caller should not do this;
+ *                 the NULL is tolerated
+ * @param signal_stale set to #MHD_YES if the nonce is stale to add 
'stale=true'
+ *                     to the authentication header, this instructs the client
+ *                     to retry immediately with the new nonce and the same
+ *                     credentials, without asking user for the new password
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
+ * @param userhash_support if set to non-zero value (#MHD_YES) then support of
+ *                         userhash is indicated, the client may provide
+ *                         hash("username:realm") instead of username in
+ *                         clear text; note that client is allowed to provide
+ *                         the username in cleartext even if this parameter set
+ *                         to non-zero
+ * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
+ *                    added, indicating for the client that UTF-8 encoding
+ *                    is preferred
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @note Available since #MHD_VERSION 0x00097526
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_queue_auth_required_response3 (struct MHD_Connection *connection,
+                                   const char *realm,
+                                   const char *opaque,
+                                   const char *domain,
+                                   struct MHD_Response *response,
+                                   int signal_stale,
+                                   enum MHD_DigestAuthMultiQOP qop,
+                                   enum MHD_DigestAuthMultiAlgo3 algo,
+                                   int userhash_support,
+                                   int prefer_utf8);
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed after call of this function.
+ *
  * @param connection The MHD connection structure
  * @param realm the realm presented to the client
  * @param opaque string to user for opaque value
@@ -5132,6 +5241,7 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
  * @param algo digest algorithm to use
  * @return #MHD_YES on success, #MHD_NO otherwise
  * @note Available since #MHD_VERSION 0x00096200
+ * @deprecated use MHD_queue_auth_required_response3()
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_Result
@@ -5148,6 +5258,9 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection 
*connection,
  * For now uses MD5 (for backwards-compatibility). Still, if you
  * need to be sure, use #MHD_queue_auth_fail_response2().
  *
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed after call of this function.
+ *
  * @param connection The MHD connection structure
  * @param realm the realm presented to the client
  * @param opaque string to user for opaque value
@@ -5157,8 +5270,8 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection 
*connection,
  * @param signal_stale #MHD_YES if the nonce is stale to add
  *        'stale=true' to the authentication header
  * @return #MHD_YES on success, #MHD_NO otherwise
+ * @deprecated use MHD_queue_auth_required_response3()
  * @ingroup authentication
- * @deprecated use MHD_queue_auth_fail_response2()
  */
 _MHD_EXTERN enum MHD_Result
 MHD_queue_auth_fail_response (struct MHD_Connection *connection,
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index b6a22785..f8f06d6d 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -30,6 +30,7 @@
 #include "platform.h"
 #include "mhd_limits.h"
 #include "internal.h"
+#include "response.h"
 #include "md5.h"
 #include "sha256.h"
 #include "mhd_mono_clock.h"
@@ -157,6 +158,16 @@
  */
 #define _MHD_SESS_TOKEN "-sess"
 
+/**
+ * The "auth" token for QOP
+ */
+#define MHD_TOKEN_AUTH_ "auth"
+
+/**
+ * The "auth-int" token for QOP
+ */
+#define MHD_TOKEN_AUTH_INT_ "auth-int"
+
 /**
  * The required prefix of parameter with the extended notation
  */
@@ -192,6 +203,73 @@ enum MHD_CheckNonceNC_
 };
 
 
+/**
+ * Get base hash calculation algorithm from #MHD_DigestAuthAlgo3 value.
+ * @param algo3 the MHD_DigestAuthAlgo3 value
+ * @return the base hash calculation algorithm
+ */
+_MHD_static_inline enum MHD_DigestBaseAlgo
+get_base_digest_algo (enum MHD_DigestAuthAlgo3 algo3)
+{
+  unsigned int base_algo;
+
+  base_algo =
+    ((unsigned int) algo3)
+    & ~((unsigned int)
+        (MHD_DIGEST_AUTH_ALGO3_NON_SESSION
+         | MHD_DIGEST_AUTH_ALGO3_NON_SESSION));
+  return (enum MHD_DigestBaseAlgo) base_algo;
+}
+
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * Internal inline version.
+ * @param algo3 the algorithm to check
+ * @return the size of the digest or zero if the input value is not
+ *         recognised/valid
+ */
+_MHD_static_inline size_t
+digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
+{
+  mhd_assert (MHD_MD5_DIGEST_SIZE == MD5_DIGEST_SIZE);
+  mhd_assert (MHD_SHA256_DIGEST_SIZE == SHA256_DIGEST_SIZE);
+  /* Both MD5 and SHA-256 must not be specified at the same time */
+  mhd_assert ( (0 == (((unsigned int) algo3)   \
+                      & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) || \
+               (0 == (((unsigned int) algo3)   \
+                      & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) );
+  if (0 != (((unsigned int) algo3)
+            & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5)))
+    return MHD_MD5_DIGEST_SIZE;
+  else if (0 != (((unsigned int) algo3)
+                 & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)))
+    return MHD_SHA256_DIGEST_SIZE;
+
+  return 0; /* Wrong input */
+}
+
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * The size of the digest specifies the size of the userhash, userdigest
+ * and other parameters which size depends on used hash algorithm.
+ * @param algo3 the algorithm to check
+ * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ *         #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not
+ *         recognised/valid
+ * @note Available since #MHD_VERSION 0x00097526
+ * @ingroup authentication
+ */
+_MHD_EXTERN size_t
+MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
+{
+  return digest_get_hash_size (algo3);
+}
+
+
 /**
  * Digest context data
  */
@@ -1904,18 +1982,22 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam 
*param,
 /**
  * Authenticates the authorization header sent by the client
  *
- * @param connection The MHD connection structure
- * @param[in,out] da digest algorithm to use for checking (written to as
- *         part of the calculations, but the values left in the struct
- *         are not actually expected to be useful for the caller)
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param digest An optional binary hash
- *     of the precalculated hash value "username:realm:password"
- *     (must contain "digest_get_size(da)" bytes or be NULL)
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
+ * @param connection the MHD connection structure
+ * @param realm the realm presented to the client
+ * @param username the username needs to be authenticated
+ * @param password the password used in the authentication
+ * @param userdigest the optional precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *            #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithms to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer,
  *                  to be free if not NULL upon return
  * @return #MHD_DAUTH_OK if authenticated,
@@ -1924,16 +2006,20 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam 
*param,
  */
 static enum MHD_DigestAuthResult
 digest_auth_check_all_inner (struct MHD_Connection *connection,
-                             struct DigestAlgorithm *da,
                              const char *realm,
                              const char *username,
                              const char *password,
-                             const uint8_t *digest,
+                             const uint8_t *userdigest,
                              unsigned int nonce_timeout,
+                             uint32_t max_nc,
+                             enum MHD_DigestAuthMultiQOP mqop,
+                             enum MHD_DigestAuthMultiAlgo3 malgo3,
                              char **pbuf)
 {
   struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
-  const unsigned int digest_size = digest_get_size (da);
+  enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */
+  struct DigestAlgorithm da;
+  unsigned int digest_size;
   uint8_t hash1_bin[MAX_DIGEST];
   uint8_t hash2_bin[MAX_DIGEST];
 #if 0
@@ -1960,6 +2046,28 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   if (NULL == params)
     return MHD_DAUTH_WRONG_HEADER;
 
+  /* ** Initial parameters checks and setup ** */
+  if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop)
+    MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
+
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The 'session' algorithms are not supported.\n"));
+#endif /* HAVE_MESSAGES */
+    return MHD_DAUTH_WRONG_ALGO;
+  }
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_MD5;
+  else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256;
+  else
+    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
+  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
+    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
+  digest_size = digest_get_size (&da);
+
   /* ** A quick check for presence of all required parameters ** */
 
   if ((NULL == params->username.value.str) &&
@@ -1980,7 +2088,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 
   if (NULL == params->realm.value.str)
     return MHD_DAUTH_WRONG_HEADER;
-  else if (((NULL == digest) || params->userhash) &&
+  else if (((NULL == userdigest) || params->userhash) &&
            (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
     return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in 
hash calculations */
 
@@ -2029,16 +2137,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* ** Check simple parameters match ** */
 
   /* Check 'algorithm' */
-  if (1)
-  {
-    const enum MHD_DigestAuthAlgo3 r_algo = get_rq_algo (params);
-    const enum MHD_DigestBaseAlgo p_algo = da->algo;
-    if ( (! ((MHD_DIGEST_AUTH_ALGO3_MD5 == r_algo) &&
-             (MHD_DIGEST_BASE_ALGO_MD5 == p_algo))) &&
-         (! ((MHD_DIGEST_AUTH_ALGO3_SHA256 == r_algo) &&
-             (MHD_DIGEST_BASE_ALGO_SHA256 == p_algo))) )
-      return MHD_DAUTH_WRONG_ALGO;
-  }
+  /* The 'algorithm' was checked at the start of the function */
   /* 'algorithm' valid */
 
   /* Check 'qop' */
@@ -2089,11 +2188,11 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   else
   { /* Userhash */
     mhd_assert (NULL != params->username.value.str);
-    digest_init (da);
-    digest_update (da, username, username_len);
-    digest_update_with_colon (da);
-    digest_update (da, realm, realm_len);
-    digest_calc_hash (da, hash1_bin);
+    digest_init (&da);
+    digest_update (&da, username, username_len);
+    digest_update_with_colon (&da);
+    digest_update (&da, realm, realm_len);
+    digest_calc_hash (&da, hash1_bin);
     mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1));
     MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
     if (! is_param_equal_caseless (&params->username, tmp1, 2 * digest_size))
@@ -2127,7 +2226,10 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 #endif
     return MHD_DAUTH_WRONG_HEADER;   /* invalid nc value */
   }
+  if ((0 != max_nc) && (max_nc < nci))
+    return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
   /* Got 'nc' digital value */
+
   /* Get 'nonce' with basic checks */
   unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
@@ -2183,7 +2285,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     {
 #ifdef HAVE_MESSAGES
       MHD_DLOG (daemon,
-                _ ("Received nonce that technically valid, but was not "
+                _ ("Received nonce that was not "
                    "generated by MHD. This may indicate an attack 
attempt.\n"));
 #endif
       return MHD_DAUTH_NONCE_WRONG;
@@ -2196,9 +2298,9 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* ** Build H(A2) and check URI match in the header and in the request ** */
 
   /* Get 'uri' */
-  digest_init (da);
-  digest_update_str (da, connection->method);
-  digest_update_with_colon (da);
+  digest_init (&da);
+  digest_update_str (&da, connection->method);
+  digest_update_with_colon (&da);
 #if 0
   /* TODO: add support for "auth-int" */
   digest_update_str (da, hentity);
@@ -2209,37 +2311,37 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
 
-  digest_update (da, unq_copy.str, unq_copy.len);
+  digest_update (&da, unq_copy.str, unq_copy.len);
   /* The next check will modify copied URI string */
   if (! check_uri_match (connection, unq_copy.str, unq_copy.len))
     return MHD_DAUTH_WRONG_URI;
-  digest_calc_hash (da, hash2_bin);
+  digest_calc_hash (&da, hash2_bin);
   /* Got H(A2) */
 
   /* ** Build H(A1) ** */
-  if (NULL == digest)
+  if (NULL == userdigest)
   {
-    digest_init (da);
-    digest_update (da, (const uint8_t *) username, username_len);
-    digest_update_with_colon (da);
-    digest_update (da, (const uint8_t *) realm, realm_len);
-    digest_update_with_colon (da);
-    digest_update_str (da, password);
-    digest_calc_hash (da, hash1_bin);
+    digest_init (&da);
+    digest_update (&da, (const uint8_t *) username, username_len);
+    digest_update_with_colon (&da);
+    digest_update (&da, (const uint8_t *) realm, realm_len);
+    digest_update_with_colon (&da);
+    digest_update_str (&da, password);
+    digest_calc_hash (&da, hash1_bin);
   }
   /* TODO: support '-sess' versions */
   /* Got H(A1) */
 
   /* **  Check 'response' ** */
 
-  digest_init (da);
+  digest_init (&da);
   /* Update digest with H(A1) */
   mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1));
-  if (NULL == digest)
+  if (NULL == userdigest)
     MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
   else
-    MHD_bin_to_hex (digest, digest_size, tmp1);
-  digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
+    MHD_bin_to_hex (userdigest, digest_size, tmp1);
+  digest_update (&da, (const uint8_t *) tmp1, digest_size * 2);
 
   /* H(A1) is not needed anymore, reuse the buffer.
    * Use hash1_bin for the client's 'response' decoded to binary form. */
@@ -2251,46 +2353,46 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     return MHD_DAUTH_RESPONSE_WRONG;
 
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'nonce' text value */
   unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'nc' text value */
   unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'cnonce' value */
   unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'qop' value */
   unq_res = get_unquoted_param (&params->qop, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with H(A2) */
   MHD_bin_to_hex (hash2_bin, digest_size, tmp1);
-  digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
+  digest_update (&da, (const uint8_t *) tmp1, digest_size * 2);
 
   /* H(A2) is not needed anymore, reuse the buffer.
    * Use hash2_bin for the calculated response in binary form */
-  digest_calc_hash (da, hash2_bin);
+  digest_calc_hash (&da, hash2_bin);
 
   if (0 != memcmp (hash1_bin, hash2_bin, digest_size))
     return MHD_DAUTH_RESPONSE_WRONG;
@@ -2307,7 +2409,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                    connection->headers_received,
                    realm,
                    realm_len,
-                   da,
+                   &da,
                    tmp1);
 
   if (! is_param_equal (&params->nonce, tmp1,
@@ -2322,37 +2424,46 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 /**
  * Authenticates the authorization header sent by the client
  *
- * @param connection The MHD connection structure
- * @param[in,out] da digest algorithm to use for checking (written to as
- *         part of the calculations, but the values left in the struct
- *         are not actually expected to be useful for the caller)
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param digest An optional binary hash
- *     of the precalculated hash value "username:realm:password"
- *     (must contain "digest_get_size(da)" bytes or be NULL)
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
+ * @param connection the MHD connection structure
+ * @param realm the realm presented to the client
+ * @param username the username needs to be authenticated
+ * @param password the password used in the authentication
+ * @param userdigest the optional precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *            #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithms to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @return #MHD_DAUTH_OK if authenticated,
  *         error code otherwise.
  * @ingroup authentication
  */
 static enum MHD_DigestAuthResult
 digest_auth_check_all (struct MHD_Connection *connection,
-                       struct DigestAlgorithm *da,
                        const char *realm,
                        const char *username,
                        const char *password,
-                       const uint8_t *digest,
-                       unsigned int nonce_timeout)
+                       const uint8_t *userdigest,
+                       unsigned int nonce_timeout,
+                       uint32_t max_nc,
+                       enum MHD_DigestAuthMultiQOP mqop,
+                       enum MHD_DigestAuthMultiAlgo3 malgo3)
 {
   enum MHD_DigestAuthResult res;
   char *buf;
 
   buf = NULL;
-  res = digest_auth_check_all_inner (connection, da, realm, username, password,
-                                     digest, nonce_timeout, &buf);
+  res = digest_auth_check_all_inner (connection, realm, username, password,
+                                     userdigest,
+                                     nonce_timeout,
+                                     max_nc, mqop, malgo3,
+                                     &buf);
   if (NULL != buf)
     free (buf);
 
@@ -2402,10 +2513,17 @@ MHD_digest_auth_check (struct MHD_Connection 
*connection,
  * @param username the username needs to be authenticated
  * @param password the password used in the authentication
  * @param nonce_timeout the nonce validity duration in seconds
- * @param algo the digest algorithms allowed for verification
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097513
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -2414,87 +2532,84 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
                         const char *username,
                         const char *password,
                         unsigned int nonce_timeout,
-                        enum MHD_DigestAuthAlgorithm algo)
+                        uint32_t max_nc,
+                        enum MHD_DigestAuthMultiQOP mqop,
+                        enum MHD_DigestAuthMultiAlgo3 malgo3)
 {
-  struct DigestAlgorithm da;
-
   mhd_assert (NULL != password);
 
-  if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
-
   return digest_auth_check_all (connection,
-                                &da,
                                 realm,
                                 username,
                                 password,
                                 NULL,
-                                nonce_timeout);
+                                nonce_timeout,
+                                max_nc,
+                                mqop,
+                                malgo3);
 }
 
 
 /**
- * Authenticates the authorization header sent by the client.
+ * Authenticates the authorization header sent by the client by using
+ * hash of "username:realm:password".
  *
  * @param connection the MHD connection structure
- * @param realm the realm to be used for authorization of the client
+ * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
- * @param digest the pointer to the binary digest for the precalculated hash
- *        value "username:realm:password" with specified @a algo
- * @param digest_size the number of bytes in @a digest (the size must match
- *        @a algo!)
- * @param nonce_timeout the nonce validity duration in seconds
- * @param algo digest algorithms allowed for verification
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param userdigest_size the size of the @a userdigest in bytes, must match 
the
+ *                        hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
+ *                        #MHD_SHA256_DIGEST_SIZE)
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based
+ *               algorithms cannot be used at the same time for this function
+ *               as @a userdigest_size must match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097513
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
 MHD_digest_auth_check_digest3 (struct MHD_Connection *connection,
                                const char *realm,
                                const char *username,
-                               const uint8_t *digest,
-                               size_t digest_size,
+                               const void *userdigest,
+                               size_t userdigest_size,
                                unsigned int nonce_timeout,
-                               enum MHD_DigestAuthAlgorithm algo)
+                               uint32_t max_nc,
+                               enum MHD_DigestAuthMultiQOP mqop,
+                               enum MHD_DigestAuthMultiAlgo3 malgo3)
 {
-  struct DigestAlgorithm da;
-
-  mhd_assert (NULL != digest);
-  if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
-
-  if (digest_get_size (&da) != digest_size)
-    MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */
+  if (((unsigned int) (MHD_DIGEST_BASE_ALGO_MD5
+                       | MHD_DIGEST_BASE_ALGO_SHA256)) ==
+      (((unsigned int) malgo3) & (MHD_DIGEST_BASE_ALGO_MD5
+                                  | MHD_DIGEST_BASE_ALGO_SHA256)))
+    MHD_PANIC (_ ("Wrong 'malgo3' value, both MD5 and SHA-256 specified, "
+                  "API violation"));
+
+  if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) !=
+      userdigest_size)
+    MHD_PANIC (_ ("Wrong 'userdigest_size' value, not matching 'malgo3, "
+                  "API violation"));
 
   return digest_auth_check_all (connection,
-                                &da,
                                 realm,
                                 username,
                                 NULL,
-                                digest,
-                                nonce_timeout);
+                                (const uint8_t *) userdigest,
+                                nonce_timeout,
+                                max_nc,
+                                mqop,
+                                malgo3);
 }
 
 
@@ -2523,12 +2638,24 @@ MHD_digest_auth_check2 (struct MHD_Connection 
*connection,
                         enum MHD_DigestAuthAlgorithm algo)
 {
   enum MHD_DigestAuthResult res;
+  enum MHD_DigestAuthMultiAlgo3 malgo3;
+
+  if (MHD_DIGEST_ALG_AUTO == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+  else if (MHD_DIGEST_ALG_MD5 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (MHD_DIGEST_ALG_SHA256 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
+
   res = MHD_digest_auth_check3 (connection,
                                 realm,
                                 username,
                                 password,
                                 nonce_timeout,
-                                algo);
+                                0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                malgo3);
   if (MHD_DAUTH_OK == res)
     return MHD_YES;
   else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res))
@@ -2567,6 +2694,16 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection 
*connection,
                                enum MHD_DigestAuthAlgorithm algo)
 {
   enum MHD_DigestAuthResult res;
+  enum MHD_DigestAuthMultiAlgo3 malgo3;
+
+  if (MHD_DIGEST_ALG_AUTO == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+  else if (MHD_DIGEST_ALG_MD5 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (MHD_DIGEST_ALG_SHA256 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
 
   res = MHD_digest_auth_check_digest3 (connection,
                                        realm,
@@ -2574,7 +2711,8 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection 
*connection,
                                        digest,
                                        digest_size,
                                        nonce_timeout,
-                                       algo);
+                                       0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                       malgo3);
   if (MHD_DAUTH_OK == res)
     return MHD_YES;
   else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res))
@@ -2622,47 +2760,94 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
 /**
  * Queues a response to request authentication from the client
  *
- * @param connection The MHD connection structure
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed (by #MHD_destroy_response()) after call of
+ * this function.
+ *
+ * @param connection the MHD connection structure
  * @param realm the realm presented to the client
- * @param opaque string to user for opaque value
- * @param response reply to send; should contain the "access denied"
- *        body; note that this function will set the "WWW Authenticate"
- *        header and that the caller should not do this; the NULL is tolerated
- * @param signal_stale #MHD_YES if the nonce is stale to add
- *        'stale=true' to the authentication header
- * @param algo digest algorithm to use
+ * @param opaque the string for opaque value, can be NULL, but NULL is
+ *               not recommended for better compatibility with clients
+ * @param domain the optional space-separated list of URIs for which the
+ *               same authorisation could be used, URIs can be in form
+ *               "path-absolute" (the path for the same host with initial 
slash)
+ *               or in form "absolute-URI" (the full path with protocol), in
+ *               any case client may assume that any URI which starts with
+ *               any of specified URI is in the same "protection space";
+ *               could be NULL (clients typically assume that the same
+ *               credentials could be used for any URI on the same host)
+ * @param response the reply to send; should contain the "access denied"
+ *                 body; note that this function sets the "WWW Authenticate"
+ *                 header and that the caller should not do this;
+ *                 the NULL is tolerated
+ * @param signal_stale set to #MHD_YES if the nonce is stale to add 
'stale=true'
+ *                     to the authentication header, this instructs the client
+ *                     to retry immediately with the new nonce and the same
+ *                     credentials, without asking user for the new password
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
+ * @param userhash_support if set to non-zero value (#MHD_YES) then support of
+ *                         userhash is indicated, the client may provide
+ *                         hash("username:realm") instead of username in
+ *                         clear text; note that client is allowed to provide
+ *                         the username in cleartext even if this parameter set
+ *                         to non-zero
+ * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
+ *                    added, indicating for the client that UTF-8 encoding
+ *                    is preferred
  * @return #MHD_YES on success, #MHD_NO otherwise
- * @note Available since #MHD_VERSION 0x00096200
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_Result
-MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
-                               const char *realm,
-                               const char *opaque,
-                               struct MHD_Response *response,
-                               int signal_stale,
-                               enum MHD_DigestAuthAlgorithm algo)
+MHD_queue_auth_required_response3 (struct MHD_Connection *connection,
+                                   const char *realm,
+                                   const char *opaque,
+                                   const char *domain,
+                                   struct MHD_Response *response,
+                                   int signal_stale,
+                                   enum MHD_DigestAuthMultiQOP mqop,
+                                   enum MHD_DigestAuthMultiAlgo3 malgo3,
+                                   int userhash_support,
+                                   int prefer_utf8)
 {
-  enum MHD_Result ret;
-  int hlen;
-
+  static const char prefix_realm[] = "realm=\"";
+  static const char prefix_qop[] = "qop=\"";
+  static const char prefix_algo[] = "algorithm=";
+  static const char prefix_nonce[] = "nonce=\"";
+  static const char prefix_opaque[] = "opaque=\"";
+  static const char prefix_domain[] = "domain=\"";
+  static const char str_charset[] = "charset=UTF-8";
+  static const char str_userhash[] = "userhash=true";
+  static const char str_stale[] = "stale=true";
+  enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */
+  size_t realm_len;
+  size_t opaque_len;
+  size_t domain_len;
+  size_t buf_size;
+  char *buf;
+  size_t p; /* The position in the buffer */
   struct DigestAlgorithm da;
 
-  if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))
   {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The 'session' algorithms are not supported.\n"));
+#endif /* HAVE_MESSAGES */
+    return MHD_NO;
   }
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_MD5;
+  else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256;
   else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
+    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
 
-  if (NULL == response)
-    return MHD_NO;
+  if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop)
+    MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
 
   if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
   {
@@ -2673,89 +2858,256 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection 
*connection,
     return MHD_NO;
   }
 
-  if (1)
-  {
-    char nonce[NONCE_STD_LEN (MAX_DIGEST) + 1];
+  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
 
-    /* VLA_CHECK_LEN_DIGEST (digest_get_size (&da)); */
-    if (! calculate_add_nonce_with_retry (connection, realm, &da, nonce))
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
-                _ ("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,
-                          0,
-                          "Digest 
realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
-                          realm,
-                          nonce,
-                          opaque,
-                          digest_get_algo_name (&da),
-                          signal_stale
-                          ? ",stale=\"true\""
-                          : "");
-    if (hlen > 0)
-    {
-      char *header;
+  /* Calculate required size */
+  buf_size = 0;
+  /* 'Digest ' */
+  buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */
+  /* 'realm="xxxx", ' */
+  realm_len = strlen (realm);
+  if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len)
+    return MHD_NO;
+  if ((NULL != memchr (realm, '\r', realm_len)) ||
+      (NULL != memchr (realm, '\n', realm_len)))
+    return MHD_NO;
+  buf_size += realm_len * 2; /* Quoting may double the size */
+  /* 'qop="xxxx", ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */
+  buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
+  /* 'algorithm="xxxx", ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */
+  if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+    buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
+  else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
+    buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+  else
+    mhd_assert (0);
+  /* 'nonce="xxxx", ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */
+  buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */
+  /* 'opaque="xxxx", ' */
+  if (NULL != opaque)
+  {
+    buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */
+    opaque_len = strlen (opaque);
+    if ((NULL != memchr (opaque, '\r', opaque_len)) ||
+        (NULL != memchr (opaque, '\n', opaque_len)))
+      return MHD_NO;
+    buf_size += opaque_len * 2; /* Quoting may double the size */
+  }
+  else
+    opaque_len = 0;
+  /* 'domain="xxxx", ' */
+  if (NULL != domain)
+  {
+    buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */
+    domain_len = strlen (domain);
+    if ((NULL != memchr (domain, '\r', domain_len)) ||
+        (NULL != memchr (domain, '\n', domain_len)))
+      return MHD_NO;
+    buf_size += domain_len * 2; /* Quoting may double the size */
+  }
+  else
+    domain_len = 0;
+  /* 'charset=UTF-8' */
+  if (MHD_NO != prefer_utf8)
+    buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */
+  /* 'userhash=true' */
+  if (MHD_NO != userhash_support)
+    buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */
+  /* 'stale=true' */
+  if (MHD_NO != signal_stale)
+    buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */
+
+  /* The calculated length is for string ended with ", ". One character will
+   * be used for zero-termination, the last one will not be used. */
+
+  /* Allocate the buffer */
+  buf = malloc (buf_size);
+  if (NULL == buf)
+    return MHD_NO;
 
-      header = MHD_calloc_ (1,
-                            (size_t) hlen + 1);
-      if (NULL == header)
-      {
+  /* Build the challenge string */
+  p = 0;
+  /* 'Digest: ' */
+  memcpy (buf + p, _MHD_AUTH_DIGEST_BASE,
+          MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE));
+  p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE);
+  buf[p++] = ' ';
+  /* 'realm="xxxx", ' */
+  memcpy (buf + p, prefix_realm,
+          MHD_STATICSTR_LEN_ (prefix_realm));
+  p += MHD_STATICSTR_LEN_ (prefix_realm);
+  mhd_assert ((buf_size - p) >= (realm_len * 2));
+  p += MHD_str_quote (realm, realm_len, buf + p, buf_size - p);
+  buf[p++] = '\"';
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'qop="xxxx", ' */
+  memcpy (buf + p, prefix_qop,
+          MHD_STATICSTR_LEN_ (prefix_qop));
+  p += MHD_STATICSTR_LEN_ (prefix_qop);
+  memcpy (buf + p, MHD_TOKEN_AUTH_,
+          MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_));
+  p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
+  buf[p++] = '\"';
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'algorithm="xxxx", ' */
+  memcpy (buf + p, prefix_algo,
+          MHD_STATICSTR_LEN_ (prefix_algo));
+  p += MHD_STATICSTR_LEN_ (prefix_algo);
+  if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+  {
+    memcpy (buf + p, _MHD_MD5_TOKEN,
+            MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN));
+    p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
+  }
+  else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
+  {
+    memcpy (buf + p, _MHD_SHA256_TOKEN,
+            MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN));
+    p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+  }
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'nonce="xxxx", ' */
+  memcpy (buf + p, prefix_nonce,
+          MHD_STATICSTR_LEN_ (prefix_nonce));
+  p += MHD_STATICSTR_LEN_ (prefix_nonce);
+  mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (&da))));
+  if (! calculate_add_nonce_with_retry (connection, realm, &da, buf + p))
+  {
 #ifdef HAVE_MESSAGES
-        MHD_DLOG (connection->daemon,
-                  _ ("Failed to allocate memory for auth response header.\n"));
+    MHD_DLOG (connection->daemon,
+              _ ("Could not register nonce. Client's requests with this "
+                 "nonce will be always 'stale'. Probably clients' requests "
+                 "are too intensive.\n"));
 #endif /* HAVE_MESSAGES */
-        return MHD_NO;
-      }
-
-      if (MHD_snprintf_ (header,
-                         (size_t) hlen + 1,
-                         "Digest 
realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
-                         realm,
-                         nonce,
-                         opaque,
-                         digest_get_algo_name (&da),
-                         signal_stale
-                         ? ",stale=\"true\""
-                         : "") == hlen)
-        ret = MHD_add_response_header (response,
-                                       MHD_HTTP_HEADER_WWW_AUTHENTICATE,
-                                       header);
-      else
-        ret = MHD_NO;
-#if 0
-      if ( (MHD_NO != ret) && (AND in state : 100 continue aborting ...))
-        ret = MHD_add_response_header (response,
-                                       MHD_HTTP_HEADER_CONNECTION,
-                                       "close");
-#endif
-      free (header);
-    }
-    else
-      ret = MHD_NO;
+    (void) 0; /* Mute compiler warning for builds without messages */
   }
-
-  if (MHD_NO != ret)
+  p += NONCE_STD_LEN (digest_get_size (&da));
+  buf[p++] = '\"';
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'opaque="xxxx", ' */
+  if (NULL != opaque)
   {
-    ret = MHD_queue_response (connection,
-                              MHD_HTTP_UNAUTHORIZED,
-                              response);
+    memcpy (buf + p, prefix_opaque,
+            MHD_STATICSTR_LEN_ (prefix_opaque));
+    p += MHD_STATICSTR_LEN_ (prefix_opaque);
+    mhd_assert ((buf_size - p) >= (opaque_len * 2));
+    p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p);
+    buf[p++] = '\"';
+    buf[p++] = ',';
+    buf[p++] = ' ';
   }
-  else
+  /* 'domain="xxxx", ' */
+  if (NULL != domain)
+  {
+    memcpy (buf + p, prefix_domain,
+            MHD_STATICSTR_LEN_ (prefix_domain));
+    p += MHD_STATICSTR_LEN_ (prefix_domain);
+    mhd_assert ((buf_size - p) >= (domain_len * 2));
+    p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p);
+    buf[p++] = '\"';
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  /* 'charset=UTF-8' */
+  if (MHD_NO != prefer_utf8)
+  {
+    memcpy (buf + p, str_charset,
+            MHD_STATICSTR_LEN_ (str_charset));
+    p += MHD_STATICSTR_LEN_ (str_charset);
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  /* 'userhash=true' */
+  if (MHD_NO != userhash_support)
+  {
+    memcpy (buf + p, str_userhash,
+            MHD_STATICSTR_LEN_ (str_userhash));
+    p += MHD_STATICSTR_LEN_ (str_userhash);
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  /* 'stale=true' */
+  if (MHD_NO != signal_stale)
+  {
+    memcpy (buf + p, str_stale,
+            MHD_STATICSTR_LEN_ (str_stale));
+    p += MHD_STATICSTR_LEN_ (str_stale);
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  mhd_assert (buf_size >= p);
+  /* The build string ends with ", ". Replace comma with zero-termination. */
+  --p;
+  buf[--p] = 0;
+
+  if (! MHD_add_response_entry_no_check_ (response, MHD_HEADER_KIND,
+                                          MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+                                          MHD_STATICSTR_LEN_ ( \
+                                            MHD_HTTP_HEADER_WWW_AUTHENTICATE),
+                                          buf, p))
   {
 #ifdef HAVE_MESSAGES
     MHD_DLOG (connection->daemon,
               _ ("Failed to add Digest auth header.\n"));
 #endif /* HAVE_MESSAGES */
+    free (buf);
+    return MHD_NO;
   }
-  return ret;
+  free (buf);
+
+  return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
+}
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param opaque string to user for opaque value
+ * @param response reply to send; should contain the "access denied"
+ *        body; note that this function will set the "WWW Authenticate"
+ *        header and that the caller should not do this; the NULL is tolerated
+ * @param signal_stale #MHD_YES if the nonce is stale to add
+ *        'stale=true' to the authentication header
+ * @param algo digest algorithm to use
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @note Available since #MHD_VERSION 0x00096200
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
+                               const char *realm,
+                               const char *opaque,
+                               struct MHD_Response *response,
+                               int signal_stale,
+                               enum MHD_DigestAuthAlgorithm algo)
+{
+  enum MHD_DigestAuthMultiAlgo3 algo3;
+
+  if (MHD_DIGEST_ALG_MD5 == algo)
+    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (MHD_DIGEST_ALG_SHA256 == algo)
+    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
+    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+  else
+    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
+
+  return MHD_queue_auth_required_response3 (connection, realm, opaque,
+                                            NULL, response, signal_stale,
+                                            MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                            algo3,
+                                            0, 0);
 }
 
 
diff --git a/src/testcurl/test_digestauth_concurrent.c 
b/src/testcurl/test_digestauth_concurrent.c
index 9a2f6227..c80bf382 100644
--- a/src/testcurl/test_digestauth_concurrent.c
+++ b/src/testcurl/test_digestauth_concurrent.c
@@ -312,8 +312,9 @@ ahc_echo (void *cls,
                                   realm,
                                   username,
                                   password,
-                                  300,
-                                  MHD_DIGEST_ALG_MD5);
+                                  50 * TIMEOUTS_VAL,
+                                  0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                  MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
   MHD_free (username);
   if (ret_e != MHD_DAUTH_OK)
   {
diff --git a/src/testcurl/test_digestauth_emu_ext.c 
b/src/testcurl/test_digestauth_emu_ext.c
index f4a0b9e8..468279ca 100644
--- a/src/testcurl/test_digestauth_emu_ext.c
+++ b/src/testcurl/test_digestauth_emu_ext.c
@@ -483,7 +483,9 @@ ahc_echo (void *cls,
 
     check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME,
                                         PASSWORD_VALUE,
-                                        300, MHD_DIGEST_ALG_MD5);
+                                        50 * TIMEOUTS_VAL,
+                                        0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                        MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
 
     switch (check_res)
     {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]