gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] branch master updated (9eb7b4de -> 34059c30)


From: gnunet
Subject: [libmicrohttpd] branch master updated (9eb7b4de -> 34059c30)
Date: Sat, 30 Jul 2022 21:29:22 +0200

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

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 9eb7b4de -fix typos
     new 5aa16f7e digestauth: added dynamic detection and use of the algo 
specified by client
     new d1b181d4 configure: cosmetics
     new 3f583074 configure: report in summary whether static and shared 
version will be built
     new 41dfd74c configure: control more parameters with 'build-type'
     new c3b626ed microhttpd: improved description for MHD_OPTION_NONCE_NC_SIZE
     new 653376ca microhttpd: improved description for 
MHD_OPTION_DIGEST_AUTH_RANDOM
     new 0ecad853 Added new MHD_OPTION_DIGEST_AUTH_RANDOM_COPY option
     new 3213c703 daemon.c: changed fill value for unused members
     new 0b702341 test_digestauth2: test the new option
     new 3d75094b test_digestauth2: added testing of 'userdigest'
     new 7727073a test_digestauth2: added testing of Auth v2 API
     new fff9fa78 digestauth: implemented support for RFC 2069
     new f708f54c test_digestauth2: added testing of RFC2069 mode
     new 51bea81b microhttpd.h: sorted Digest Auth functions and enums
     new 34059c30 MHD_digest_auth_check3(): return failed parameter if it is 
known

The 15 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 configure.ac                    |  39 ++++-
 src/include/microhttpd.h        | 365 ++++++++++++++++++++++++----------------
 src/microhttpd/daemon.c         |  37 +++-
 src/microhttpd/digestauth.c     | 345 +++++++++++++++++++++++--------------
 src/microhttpd/internal.h       |   5 +
 src/testcurl/.gitignore         |  13 +-
 src/testcurl/Makefile.am        |  50 +++++-
 src/testcurl/test_digestauth2.c | 298 ++++++++++++++++++++++++--------
 8 files changed, 790 insertions(+), 362 deletions(-)

diff --git a/configure.ac b/configure.ac
index 8f87bc0f..b12bb253 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,9 +23,8 @@
 #
 AC_PREREQ([2.64])
 LT_PREREQ([2.4.0])
-AC_INIT([GNU Libmicrohttpd],[0.9.75],[libmicrohttpd@gnu.org])
+AC_INIT([GNU libmicrohttpd],[0.9.75],[libmicrohttpd@gnu.org])
 AC_CONFIG_AUX_DIR([build-aux])
-AM_INIT_AUTOMAKE([gnu] [check-news] [filename-length-max=99] [tar-v7] 
[silent-rules] [subdir-objects])
 AC_CONFIG_HEADERS([MHD_config.h])
 AC_CONFIG_MACRO_DIR([m4])
 m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl
@@ -33,9 +32,9 @@ m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl
 LIB_VERSION_CURRENT=72
 LIB_VERSION_REVISION=0
 LIB_VERSION_AGE=60
-AC_SUBST(LIB_VERSION_CURRENT)
-AC_SUBST(LIB_VERSION_REVISION)
-AC_SUBST(LIB_VERSION_AGE)
+AC_SUBST([LIB_VERSION_CURRENT])
+AC_SUBST([LIB_VERSION_REVISION])
+AC_SUBST([LIB_VERSION_AGE])
 
 
 PACKAGE_VERSION_MAJOR='m4_car(m4_unquote(m4_split(AC_PACKAGE_VERSION, [\.])))'
@@ -86,7 +85,7 @@ AS_VAR_IF([enable_build_type], ["no"], 
[enable_build_type="neutral"])
 AS_VAR_IF([enable_build_type], ["yes"], [AC_MSG_ERROR([[Missing TYPE for 
--enable-build-type=]])])
 AS_CASE([${enable_build_type}],
   [debug], [AC_MSG_RESULT([debug. Defaults: enable asserts, sanitizers (if any 
supported), debug information, compiler optimisation for debugging])],
-  [debugger], [AC_MSG_RESULT([debugger. Defaults: enable asserts, disable 
sanitizers, debug information, no compiler optimisation])],
+  [debugger], [AC_MSG_RESULT([debugger. Defaults: enable asserts, disable 
sanitizers, debug information, no compiler optimisation, static lib])],
   [neutral], [AC_MSG_RESULT([neutral. Defaults: use only user-specified 
compiler and linker flags])],
   [release], [AC_MSG_RESULT([release. Defaults: disable asserts, enable 
compiler optimisations])],
   [release-compact], [AC_MSG_RESULT([release-compact. Defaults: disable 
asserts, enable compiler optimisations for size, enable compact code])],
@@ -99,6 +98,30 @@ AS_VAR_IF([enable_build_type], ["neutral"], [:],
     AS_IF([test -z "${CFLAGS}"], [CFLAGS=""])
   ]
 )
+AS_VAR_IF([enable_build_type], ["debugger"],
+  [ # Build only static version unless something else is specified by the user
+    AS_IF([test -z "${enable_static}" || test "x${enable_static}" = "xyes"],
+      [
+        AS_IF([test -z "${enable_shared}"], 
+          [
+            enable_shared="no"
+            enable_static="yes"
+          ]
+        )
+      ],
+      [
+        AS_CASE([${enable_static}],[*libmicrohttpd*],
+          [AS_IF([test -z "${enable_shared}"], [enable_shared="no"])],
+        )
+      ]
+    )
+  ]
+)
+AS_CASE([${enable_build_type}],[debug|debugger],
+  [ AS_IF([test -z "${enable_silent_rules}"], [ enable_silent_rules="yes" ])]
+)
+
+AM_INIT_AUTOMAKE([gnu] [check-news] [filename-length-max=99] [tar-v7] 
[silent-rules] [subdir-objects])
 
 # Checks for programs.
 AC_PROG_AWK
@@ -3894,7 +3917,7 @@ 
MHD_PREPEND_FLAG_TO_VAR([fin_lib_LDFLAGS],[$MHD_LIB_LDFLAGS])
 MHD_PREPEND_FLAG_TO_VAR([fin_lib_CPPFLAGS],[$CPPFLAGS_ac])
 MHD_PREPEND_FLAG_TO_VAR([fin_lib_CFLAGS],[$CFLAGS_ac])
 MHD_PREPEND_FLAG_TO_VAR([fin_lib_LDFLAGS],[$LDFLAGS_ac])
-AC_MSG_NOTICE([Toolchain flags:
+AC_MSG_NOTICE([Toolchain settings:
   CC=$CC
   User/system/default flags:
     CPPFLAGS="$user_CPPFLAGS"
@@ -3948,6 +3971,8 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} 
Configuration Summary:
   Postproc:          ${enable_postprocessor}
   Build docs:        ${enable_doc}
   Build examples:    ${enable_examples}
+  Build static lib:  ${enable_static}
+  Build shared lib:  ${enable_shared}
   Test with libcurl: ${MSG_CURL}
 ])
 
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index c0e911c5..383bb67b 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 0x00097527
+#define MHD_VERSION 0x00097530
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -1752,11 +1752,15 @@ enum MHD_OPTION
   /**
    * Memory pointer for the random values to be used by the Digest
    * Auth module. This option should be followed by two arguments.
-   * First an integer of type  `size_t` which specifies the size
+   * First an integer of type `size_t` which specifies the size
    * of the buffer pointed to by the second argument in bytes.
+   * The recommended size is between 8 and 32. If size is four or less
+   * then security could be lowered. Sizes more then 32 (or, probably
+   * more than 16 - debatable) will not increase security.
    * Note that the application must ensure that the buffer of the
    * second argument remains allocated and unmodified while the
    * daemon is running.
+   * @sa #MHD_OPTION_DIGEST_AUTH_RANDOM_COPY
    */
   MHD_OPTION_DIGEST_AUTH_RANDOM = 17,
 
@@ -1764,6 +1768,11 @@ enum MHD_OPTION
    * Size of the internal array holding the map of the nonce and
    * the nonce counter. This option should be followed by an `unsigend int`
    * argument.
+   * The map size is 4 by default, which is enough to communicate with
+   * a single client at any given moment of time, but not enough to
+   * handle several clients simultaneously.
+   * If Digest Auth is not used, this option can be set to zero to minimise
+   * memory allocation.
    */
   MHD_OPTION_NONCE_NC_SIZE = 18,
 
@@ -1919,7 +1928,22 @@ enum MHD_OPTION
    * This option should be followed by an `int` argument.
    * @note Available since #MHD_VERSION 0x00097207
    */
-  MHD_OPTION_TLS_NO_ALPN = 34
+  MHD_OPTION_TLS_NO_ALPN = 34,
+
+  /**
+   * Memory pointer for the random values to be used by the Digest
+   * Auth module. This option should be followed by two arguments.
+   * First an integer of type `size_t` which specifies the size
+   * of the buffer pointed to by the second argument in bytes.
+   * The recommended size is between 8 and 32. If size is four or less
+   * then security could be lowered. Sizes more then 32 (or, probably
+   * more than 16 - debatable) will not increase security.
+   * An internal copy of the buffer will be made, the data do not
+   * need to be static.
+   * @sa #MHD_OPTION_DIGEST_AUTH_RANDOM
+   * @note Available since #MHD_VERSION 0x00097529
+   */
+  MHD_OPTION_DIGEST_AUTH_RANDOM_COPY = 35
 } _MHD_FIXED_ENUM;
 
 
@@ -4336,14 +4360,6 @@ MHD_destroy_post_processor (struct MHD_PostProcessor 
*pp);
  */
 #define MHD_SHA256_DIGEST_SIZE 32
 
-
-/**
- * Constant to indicate that the nonce of the provided
- * authentication code was wrong.
- * @ingroup authentication
- */
-#define MHD_INVALID_NONCE -1
-
 /**
  * Base type of hash calculation.
  * Used as part of #MHD_DigestAuthAlgo3 values.
@@ -4448,6 +4464,22 @@ enum MHD_DigestAuthAlgo3
     MHD_DIGEST_BASE_ALGO_SHA512_256 | MHD_DIGEST_AUTH_ALGO3_SESSION,
 };
 
+
+/**
+ * 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);
+
 /**
  * Digest algorithm identification, allow multiple selection.
  *
@@ -4599,8 +4631,11 @@ enum MHD_DigestAuthQOP
 
   /**
    * No QOP parameter.
-   * Match old RFC 2069 specification.
-   * Not supported by MHD for authentication.
+   * As described in old RFC 2069 original specification.
+   * This mode is not allowed by latest RFCs and should be used only to
+   * communicate with clients that do not support more modern modes (with QOP
+   * parameter).
+   * This mode is less secure than other modes and inefficient.
    */
   MHD_DIGEST_AUTH_QOP_NONE = 1 << 0,
 
@@ -4622,7 +4657,7 @@ enum MHD_DigestAuthQOP
  * #MHD_DigestAuthQOP always can be casted to #MHD_DigestAuthMultiQOP, but
  * not vice versa.
  *
- * @note Available since #MHD_VERSION 0x00097523
+ * @note Available since #MHD_VERSION 0x00097530
  */
 enum MHD_DigestAuthMultiQOP
 {
@@ -4633,9 +4668,11 @@ enum MHD_DigestAuthMultiQOP
 
   /**
    * No QOP parameter.
-   * Match old RFC 2069 specification.
-   * Not supported by MHD.
-   * Reserved value.
+   * As described in old RFC 2069 original specification.
+   * This mode is not allowed by latest RFCs and should be used only to
+   * communicate with clients that do not support more modern modes (with QOP
+   * parameter).
+   * This mode is less secure than other modes and inefficient.
    */
   MHD_DIGEST_AUTH_MULT_QOP_NONE = MHD_DIGEST_AUTH_QOP_NONE,
 
@@ -4651,6 +4688,15 @@ enum MHD_DigestAuthMultiQOP
    */
   MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT = MHD_DIGEST_AUTH_QOP_AUTH_INT,
 
+  /**
+   * The 'auth' QOP type OR the old RFC2069 (no QOP) type.
+   * In other words: any types except 'auth-int'.
+   * RFC2069-compatible mode is allowed, thus this value should be used only
+   * when it is really necessary.
+   */
+  MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT =
+    MHD_DIGEST_AUTH_QOP_NONE | MHD_DIGEST_AUTH_QOP_AUTH,
+
   /**
    * Any 'auth' QOP type ('auth' or 'auth-int').
    * Not supported by MHD.
@@ -4681,7 +4727,6 @@ struct MHD_DigestAuthInfo
   /**
    * The algorithm as defined by client.
    * Set automatically to MD5 if not specified by client.
-   * No "group" (ALGO3_ANY) values are used.
    * @warning Do not be confused with #MHD_DigestAuthAlgorithm,
    *          which uses other values!
    */
@@ -4772,6 +4817,7 @@ struct MHD_DigestAuthInfo
   uint32_t nc;
 };
 
+
 /**
  * Get information about Digest Authorization client's header.
  *
@@ -4779,6 +4825,7 @@ struct MHD_DigestAuthInfo
  * @return NULL if no valid Digest Authorization header is used in the request;
  *         a pointer to the structure with information if the valid request
  *         header found, free using #MHD_free().
+ * @sa #MHD_digest_auth_get_username3()
  * @note Available since #MHD_VERSION 0x00097519
  * @ingroup authentication
  */
@@ -4836,22 +4883,6 @@ struct MHD_DigestAuthUsernameInfo
 };
 
 
-/**
- * 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.
  *
@@ -4862,7 +4893,7 @@ MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3);
  *         #MHD_DIGEST_AUTH_UNAME_TYPE_INVALID);
  *         a pointer structure with information if the valid request header
  *         found, free using #MHD_free().
- * @sa MHD_digest_auth_get_request_info3() provides more complete information
+ * @sa #MHD_digest_auth_get_request_info3() provides more complete information
  * @note Available since #MHD_VERSION 0x00097519
  * @ingroup authentication
  */
@@ -4870,48 +4901,6 @@ _MHD_EXTERN struct MHD_DigestAuthUsernameInfo *
 MHD_digest_auth_get_username3 (struct MHD_Connection *connection);
 
 
-/**
- * Get the username from the authorization header sent by the client
- *
- * This function supports username in standard and extended notations.
- * "userhash" is not supported by this function.
- *
- * @param connection The MHD connection structure
- * @return NULL if no username could be found, username provided as
- *         "userhash" or memory allocation error occurred;
- *         a pointer to the username if found, free using #MHD_free().
- * @warning Returned value must be freed by #MHD_free().
- * @deprecated use MHD_digest_auth_get_username3()
- * @ingroup authentication
- */
-_MHD_EXTERN char *
-MHD_digest_auth_get_username (struct MHD_Connection *connection);
-
-
-/**
- * Which digest algorithm should MHD use for HTTP digest authentication?
- */
-enum MHD_DigestAuthAlgorithm
-{
-
-  /**
-   * MHD should pick (currently defaults to MD5).
-   */
-  MHD_DIGEST_ALG_AUTO = 0,
-
-  /**
-   * Force use of MD5.
-   */
-  MHD_DIGEST_ALG_MD5,
-
-  /**
-   * Force use of SHA-256.
-   */
-  MHD_DIGEST_ALG_SHA256
-
-} _MHD_FIXED_ENUM;
-
-
 /**
  * The result of digest authentication of the client.
  *
@@ -4933,6 +4922,8 @@ enum MHD_DigestAuthResult
 
   /**
    * No "Authorization" header or wrong format of the header.
+   * Also may be returned if required parameters in client Authorisation header
+   * are missing or broken (in invalid format).
    */
   MHD_DAUTH_WRONG_HEADER = -1,
 
@@ -4991,6 +4982,14 @@ enum MHD_DigestAuthResult
 /**
  * Authenticates the authorization header sent by the client.
  *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not suppoted in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
  * @param connection the MHD connection structure
  * @param realm the realm to be used for authorization of the client
  * @param username the username needs to be authenticated
@@ -5000,13 +4999,12 @@ enum MHD_DigestAuthResult
  *               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)
+ * @param mqop the QOP to use
+ * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097526
+ * @note Available since #MHD_VERSION 0x00097528
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -5024,6 +5022,14 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  * Authenticates the authorization header sent by the client by using
  * hash of "username:realm:password".
  *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not suppoted in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
  * @param connection the MHD connection structure
  * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
@@ -5038,14 +5044,15 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  *               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
+ * @param mqop the QOP to use
+ * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter;
+ *               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 0x00097526
+ * @note Available since #MHD_VERSION 0x00097528
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -5060,6 +5067,126 @@ MHD_digest_auth_check_digest3 (struct MHD_Connection 
*connection,
                                enum MHD_DigestAuthMultiAlgo3 malgo3);
 
 
+/**
+ * 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.
+ *
+ * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with
+ * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was
+ * not set, because such response should be backward-compatible with RFC 2069.
+ *
+ * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is
+ * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no
+ * 'charset'). For better compatibility with clients, it is recommended (but
+ * not required) to set @a domain to NULL in this mode.
+ *
+ * @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;
+ *               the recommended format is hex or Base64 encoded string
+ * @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
+ * @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);
+
+
+/**
+ * Constant to indicate that the nonce of the provided
+ * authentication code was wrong.
+ * Used as return code by #MHD_digest_auth_check(), #MHD_digest_auth_check2(),
+ * #MHD_digest_auth_check_digest(), #MHD_digest_auth_check_digest2().
+ * @ingroup authentication
+ */
+#define MHD_INVALID_NONCE -1
+
+
+/**
+ * Get the username from the authorization header sent by the client
+ *
+ * This function supports username in standard and extended notations.
+ * "userhash" is not supported by this function.
+ *
+ * @param connection The MHD connection structure
+ * @return NULL if no username could be found, username provided as
+ *         "userhash" or memory allocation error occurred;
+ *         a pointer to the username if found, free using #MHD_free().
+ * @warning Returned value must be freed by #MHD_free().
+ * @deprecated use MHD_digest_auth_get_username3()
+ * @ingroup authentication
+ */
+_MHD_EXTERN char *
+MHD_digest_auth_get_username (struct MHD_Connection *connection);
+
+
+/**
+ * Which digest algorithm should MHD use for HTTP digest authentication?
+ * Used as parameter for #MHD_digest_auth_check2(),
+ * #MHD_digest_auth_check_digest2(), #MHD_queue_auth_fail_response2().
+ */
+enum MHD_DigestAuthAlgorithm
+{
+
+  /**
+   * MHD should pick (currently defaults to MD5).
+   */
+  MHD_DIGEST_ALG_AUTO = 0,
+
+  /**
+   * Force use of MD5.
+   */
+  MHD_DIGEST_ALG_MD5,
+
+  /**
+   * Force use of SHA-256.
+   */
+  MHD_DIGEST_ALG_SHA256
+
+} _MHD_FIXED_ENUM;
+
+
 /**
  * Authenticates the authorization header sent by the client.
  *
@@ -5167,63 +5294,6 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
                               unsigned int nonce_timeout);
 
 
-/**
- * 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
  *
@@ -5712,8 +5782,9 @@ enum MHD_FEATURE
 
   /**
    * Get whether the early version the Digest Authorization (RFC 2069) is
-   * supported.
-   * Currently it is always not supported if Digest Auth module is built.
+   * supported (digest authorisation without QOP parameter).
+   * Since #MHD_VERSION 0x00097530 it is always supported if Digest Auth
+   * module is built.
    * @note Available since #MHD_VERSION 0x00097527
    */
   MHD_FEATURE_DIGEST_AUTH_RFC2069 = 25,
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 2f868bfb..7925111b 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -6236,10 +6236,16 @@ parse_options_va (struct MHD_Daemon *daemon,
 #endif /* HTTPS_SUPPORT */
 #ifdef DAUTH_SUPPORT
     case MHD_OPTION_DIGEST_AUTH_RANDOM:
+    case MHD_OPTION_DIGEST_AUTH_RANDOM_COPY:
       daemon->digest_auth_rand_size = va_arg (ap,
                                               size_t);
       daemon->digest_auth_random = va_arg (ap,
                                            const char *);
+      if (MHD_OPTION_DIGEST_AUTH_RANDOM_COPY == opt)
+        /* Set to some non-NULL value just to indicate that copy is required. 
*/
+        daemon->digest_auth_random_copy = daemon;
+      else
+        daemon->digest_auth_random_copy = NULL;
       break;
     case MHD_OPTION_NONCE_NC_SIZE:
       daemon->nonce_nc_size = va_arg (ap,
@@ -6440,6 +6446,7 @@ parse_options_va (struct MHD_Daemon *daemon,
           break;
         /* options taking size_t-number followed by pointer */
         case MHD_OPTION_DIGEST_AUTH_RANDOM:
+        case MHD_OPTION_DIGEST_AUTH_RANDOM_COPY:
           if (MHD_NO == parse_options (daemon,
                                        servaddr,
                                        opt,
@@ -6913,6 +6920,24 @@ MHD_start_daemon_va (unsigned int flags,
   }
 
 #ifdef DAUTH_SUPPORT
+  if (NULL != daemon->digest_auth_random_copy)
+  {
+    mhd_assert (daemon == daemon->digest_auth_random_copy);
+    daemon->digest_auth_random_copy = malloc (daemon->digest_auth_rand_size);
+    if (NULL == daemon->digest_auth_random_copy)
+    {
+#ifdef HTTPS_SUPPORT
+      if (0 != (*pflags & MHD_USE_TLS))
+        gnutls_priority_deinit (daemon->priority_cache);
+#endif /* HTTPS_SUPPORT */
+      free (daemon);
+      return NULL;
+    }
+    memcpy (daemon->digest_auth_random_copy,
+            daemon->digest_auth_random,
+            daemon->digest_auth_rand_size);
+    daemon->digest_auth_random = daemon->digest_auth_random_copy;
+  }
   if (daemon->nonce_nc_size > 0)
   {
     if ( ( (size_t) (daemon->nonce_nc_size * sizeof (struct MHD_NonceNc)))
@@ -6926,6 +6951,7 @@ MHD_start_daemon_va (unsigned int flags,
       if (0 != (*pflags & MHD_USE_TLS))
         gnutls_priority_deinit (daemon->priority_cache);
 #endif /* HTTPS_SUPPORT */
+      free (daemon->digest_auth_random_copy);
       free (daemon);
       return NULL;
     }
@@ -6942,6 +6968,7 @@ MHD_start_daemon_va (unsigned int flags,
       if (0 != (*pflags & MHD_USE_TLS))
         gnutls_priority_deinit (daemon->priority_cache);
 #endif /* HTTPS_SUPPORT */
+      free (daemon->digest_auth_random_copy);
       free (daemon);
       return NULL;
     }
@@ -6958,6 +6985,7 @@ MHD_start_daemon_va (unsigned int flags,
     if (0 != (*pflags & MHD_USE_TLS))
       gnutls_priority_deinit (daemon->priority_cache);
 #endif /* HTTPS_SUPPORT */
+    free (daemon->digest_auth_random_copy);
     free (daemon->nnc);
     free (daemon);
     return NULL;
@@ -7580,14 +7608,15 @@ MHD_start_daemon_va (unsigned int flags,
 #endif
         /* Some members must be used only in master daemon */
 #if defined(MHD_USE_THREADS)
-        memset (&d->per_ip_connection_mutex, 1,
+        memset (&d->per_ip_connection_mutex, 0x7F,
                 sizeof(d->per_ip_connection_mutex));
 #endif /* MHD_USE_THREADS */
 #ifdef DAUTH_SUPPORT
         d->nnc = NULL;
         d->nonce_nc_size = 0;
+        d->digest_auth_random_copy = NULL;
 #if defined(MHD_USE_THREADS)
-        memset (&d->nnc_lock, 1, sizeof(d->nnc_lock));
+        memset (&d->nnc_lock, 0x7F, sizeof(d->nnc_lock));
 #endif /* MHD_USE_THREADS */
 #endif /* DAUTH_SUPPORT */
 
@@ -7706,6 +7735,7 @@ free_and_fail:
 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
 #endif /* EPOLL_SUPPORT */
 #ifdef DAUTH_SUPPORT
+  free (daemon->digest_auth_random_copy);
   free (daemon->nnc);
 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
   MHD_mutex_destroy_chk_ (&daemon->nnc_lock);
@@ -8102,6 +8132,7 @@ MHD_stop_daemon (struct MHD_Daemon *daemon)
 #endif /* HTTPS_SUPPORT */
 
 #ifdef DAUTH_SUPPORT
+    free (daemon->digest_auth_random_copy);
     free (daemon->nnc);
 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
     MHD_mutex_destroy_chk_ (&daemon->nnc_lock);
@@ -8381,7 +8412,7 @@ MHD_is_feature_supported (enum MHD_FEATURE feature)
 #endif
   case MHD_FEATURE_DIGEST_AUTH_RFC2069:
 #ifdef DAUTH_SUPPORT
-    return MHD_NO;
+    return MHD_YES;
 #else
     return MHD_NO;
 #endif
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index f8f06d6d..3e5468af 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -1982,6 +1982,14 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam 
*param,
 /**
  * Authenticates the authorization header sent by the client
  *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not suppoted in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
  * @param connection the MHD connection structure
  * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
@@ -1994,10 +2002,9 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam 
*param,
  *               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 mqop the QOP to use
+ * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter
  * @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,
@@ -2017,7 +2024,8 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                              char **pbuf)
 {
   struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
-  enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */
+  enum MHD_DigestAuthAlgo3 c_algo; /**< Client's algorithm */
+  enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */
   struct DigestAlgorithm da;
   unsigned int digest_size;
   uint8_t hash1_bin[MAX_DIGEST];
@@ -2047,10 +2055,14 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     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))
+  /* Get client's algorithm */
+  c_algo = get_rq_algo (params);
+  /* Check whether client's algorithm is allowed by function parameter */
+  if (((unsigned int) c_algo) !=
+      (((unsigned int) c_algo) & ((unsigned int) malgo3)))
+    return MHD_DAUTH_WRONG_ALGO;
+  /* Check whether client's algorithm is supported */
+  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO3_SESSION))
   {
 #ifdef HAVE_MESSAGES
     MHD_DLOG (connection->daemon,
@@ -2058,77 +2070,92 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 #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)))
+  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The SHA-512/256 algorithm is not supported.\n"));
+#endif /* HAVE_MESSAGES */
+    return MHD_DAUTH_WRONG_ALGO;
+  }
+  if (! digest_setup (&da, get_base_digest_algo (c_algo)))
     MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
+  /* Check 'mqop' value */
+  c_qop = get_rq_qop (params);
+  /* Check whether client's algorithm is allowed by function parameter */
+  if (((unsigned int) c_qop) !=
+      (((unsigned int) c_qop) & ((unsigned int) mqop)))
+    return MHD_DAUTH_WRONG_QOP;
+  if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT))
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The 'auth-int' QOP is not supported.\n"));
+#endif /* HAVE_MESSAGES */
+    return MHD_DAUTH_WRONG_QOP;
+  }
+
   digest_size = digest_get_size (&da);
 
   /* ** A quick check for presence of all required parameters ** */
 
   if ((NULL == params->username.value.str) &&
       (NULL == params->username_ext.value.str))
-    return MHD_DAUTH_WRONG_HEADER;
+    return MHD_DAUTH_WRONG_USERNAME;
   else if ((NULL != params->username.value.str) &&
            (NULL != params->username_ext.value.str))
-    return MHD_DAUTH_WRONG_HEADER; /* Parameters cannot be used together */
+    return MHD_DAUTH_WRONG_USERNAME; /* Parameters cannot be used together */
   else if ((NULL != params->username_ext.value.str) &&
            (MHD_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
-    return MHD_DAUTH_WRONG_HEADER;  /* Broken extended notation */
+    return MHD_DAUTH_WRONG_USERNAME;  /* Broken extended notation */
   else if (params->userhash && (NULL == params->username.value.str))
-    return MHD_DAUTH_WRONG_HEADER;  /* Userhash cannot be used with extended 
notation */
+    return MHD_DAUTH_WRONG_USERNAME;  /* Userhash cannot be used with extended 
notation */
   else if (params->userhash && (digest_size * 2 > params->username.value.len))
-    return MHD_DAUTH_WRONG_HEADER;  /* Too few chars for correct userhash */
+    return MHD_DAUTH_WRONG_USERNAME;  /* Too few chars for correct userhash */
   else if (params->userhash && (digest_size * 4 < params->username.value.len))
-    return MHD_DAUTH_WRONG_HEADER;  /* Too many chars for correct userhash */
+    return MHD_DAUTH_WRONG_USERNAME;  /* Too many chars for correct userhash */
 
   if (NULL == params->realm.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
+    return MHD_DAUTH_WRONG_REALM;
   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 */
 
-  if (NULL == params->nc.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
-  else if (0 == params->nc.value.len)
-    return MHD_DAUTH_WRONG_HEADER;
-  else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
-    return MHD_DAUTH_WRONG_HEADER;
-
-  if (NULL == params->cnonce.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
-  else if (0 == params->cnonce.value.len)
-    return MHD_DAUTH_WRONG_HEADER;
-  else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
-    return MHD_DAUTH_TOO_LARGE;
+  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+  {
+    if (NULL == params->nc.value.str)
+      return MHD_DAUTH_WRONG_HEADER;
+    else if (0 == params->nc.value.len)
+      return MHD_DAUTH_WRONG_HEADER;
+    else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
+      return MHD_DAUTH_WRONG_HEADER;
+
+    if (NULL == params->cnonce.value.str)
+      return MHD_DAUTH_WRONG_HEADER;
+    else if (0 == params->cnonce.value.len)
+      return MHD_DAUTH_WRONG_HEADER;
+    else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
+      return MHD_DAUTH_TOO_LARGE;
+  }
 
-  if (NULL == params->qop.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
-  else if (0 == params->qop.value.len)
-    return MHD_DAUTH_WRONG_QOP;
-  else if (MHD_STATICSTR_LEN_ ("auth-int") * 2 < params->qop.value.len)
-    return MHD_DAUTH_WRONG_QOP;
+  /* The QOP parameter was checked already */
 
   if (NULL == params->uri.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
+    return MHD_DAUTH_WRONG_URI;
   else if (0 == params->uri.value.len)
     return MHD_DAUTH_WRONG_URI;
   else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
     return MHD_DAUTH_TOO_LARGE;
 
   if (NULL == params->nonce.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
+    return MHD_DAUTH_NONCE_WRONG;
   else if (0 == params->nonce.value.len)
     return MHD_DAUTH_NONCE_WRONG;
   else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len)
     return MHD_DAUTH_NONCE_WRONG;
 
   if (NULL == params->response.value.str)
-    return MHD_DAUTH_WRONG_HEADER;
+    return MHD_DAUTH_RESPONSE_WRONG;
   else if (0 == params->response.value.len)
     return MHD_DAUTH_RESPONSE_WRONG;
   else if (digest_size * 4 < params->response.value.len)
@@ -2141,9 +2168,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* 'algorithm' valid */
 
   /* Check 'qop' */
-  /* TODO: support MHD_DIGEST_AUTH_QOP_NONE and MHD_DIGEST_AUTH_QOP_AUTH_INT */
-  if (MHD_DIGEST_AUTH_QOP_AUTH != get_rq_qop (params))
-    return MHD_DAUTH_WRONG_QOP;
+  /* The 'qop' was checked at the start of the function */
   /* 'qop' valid */
 
   /* Check 'realm' */
@@ -2203,31 +2228,37 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
 
   /* Get 'nc' digital value */
-  unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
-                                &unquoted);
-  if (_MHD_UNQ_OK != unq_res)
-    return MHD_DAUTH_ERROR;
-
-  if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str,
-                                             unquoted.len,
-                                             &nci))
+  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
   {
+
+    unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
+                                  &unquoted);
+    if (_MHD_UNQ_OK != unq_res)
+      return MHD_DAUTH_ERROR;
+
+    if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str,
+                                               unquoted.len,
+                                               &nci))
+    {
 #ifdef HAVE_MESSAGES
-    MHD_DLOG (daemon,
-              _ ("Authentication failed, invalid nc format.\n"));
+      MHD_DLOG (daemon,
+                _ ("Authentication failed, invalid nc format.\n"));
 #endif
-    return MHD_DAUTH_WRONG_HEADER;   /* invalid nonce format */
-  }
-  if (0 == nci)
-  {
+      return MHD_DAUTH_WRONG_HEADER;   /* invalid nonce format */
+    }
+    if (0 == nci)
+    {
 #ifdef HAVE_MESSAGES
-    MHD_DLOG (daemon,
-              _ ("Authentication failed, invalid 'nc' value.\n"));
+      MHD_DLOG (daemon,
+                _ ("Authentication failed, invalid 'nc' value.\n"));
 #endif
-    return MHD_DAUTH_WRONG_HEADER;   /* invalid nc value */
+      return MHD_DAUTH_WRONG_HEADER;   /* invalid nc value */
+    }
+    if ((0 != max_nc) && (max_nc < nci))
+      return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
   }
-  if ((0 != max_nc) && (max_nc < nci))
-    return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
+  else
+    nci = 1; /* Force 'nc' value */
   /* Got 'nc' digital value */
 
   /* Get 'nonce' with basic checks */
@@ -2275,9 +2306,15 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     if (MHD_CHECK_NONCENC_STALE == nonce_nc_check)
     {
 #ifdef HAVE_MESSAGES
-      MHD_DLOG (daemon,
-                _ ("Stale nonce received. If this happens a lot, you should "
-                   "probably increase the size of the nonce array.\n"));
+      if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+        MHD_DLOG (daemon,
+                  _ ("Stale nonce received. If this happens a lot, you should "
+                     "probably increase the size of the nonce array.\n"));
+      else
+        MHD_DLOG (daemon,
+                  _ ("Stale nonce received. If this happens a lot, you should "
+                     "probably increase the size of the nonce array or not"
+                     "use RFC2069-compatible mode .\n"));
 #endif
       return MHD_DAUTH_NONCE_STALE;
     }
@@ -2362,30 +2399,33 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
   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);
-  /* Update digest with ':' */
-  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);
-  /* Update digest with ':' */
-  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);
-  /* Update digest with ':' */
-  digest_update_with_colon (&da);
+  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+  {
+    /* 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);
+    /* Update digest with ':' */
+    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);
+    /* Update digest with ':' */
+    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);
+    /* Update digest with ':' */
+    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);
@@ -2424,6 +2464,14 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 /**
  * Authenticates the authorization header sent by the client
  *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not suppoted in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
  * @param connection the MHD connection structure
  * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
@@ -2436,10 +2484,9 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
  *               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 mqop the QOP to use
+ * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter
  * @return #MHD_DAUTH_OK if authenticated,
  *         error code otherwise.
  * @ingroup authentication
@@ -2508,6 +2555,14 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
 /**
  * Authenticates the authorization header sent by the client.
  *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not suppoted in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
  * @param connection the MHD connection structure
  * @param realm the realm to be used for authorization of the client
  * @param username the username needs to be authenticated
@@ -2517,13 +2572,12 @@ MHD_digest_auth_check (struct MHD_Connection 
*connection,
  *               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)
+ * @param mqop the QOP to use
+ * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097526
+ * @note Available since #MHD_VERSION 0x00097528
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -2554,6 +2608,14 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  * Authenticates the authorization header sent by the client by using
  * hash of "username:realm:password".
  *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not suppoted in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
  * @param connection the MHD connection structure
  * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
@@ -2568,14 +2630,15 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  *               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
+ * @param mqop the QOP to use
+ * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter;
+ *               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 0x00097526
+ * @note Available since #MHD_VERSION 0x00097528
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -2764,10 +2827,20 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
  * reused and should be destroyed (by #MHD_destroy_response()) after call of
  * this function.
  *
+ * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with
+ * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was
+ * not set, because such response should be backward-compatible with RFC 2069.
+ *
+ * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is
+ * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no
+ * 'charset'). For better compatibility with clients, it is recommended (but
+ * not required) to set @a domain to NULL in this mode.
+ *
  * @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
+ *               not recommended for better compatibility with clients;
+ *               the recommended format is hex or Base64 encoded string
  * @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)
@@ -2784,8 +2857,7 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
  *                     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 mqop the QOP to use
  * @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
@@ -2846,9 +2918,25 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   else
     MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
 
-  if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop)
+  if (((unsigned int) mqop) !=
+      (((unsigned int) mqop) & MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT))
     MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
 
+  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
+
+  if (MHD_DIGEST_AUTH_MULT_QOP_NONE == mqop)
+  {
+#ifdef HAVE_MESSAGES
+    if ((0 != userhash_support) || (0 != prefer_utf8))
+      MHD_DLOG (connection->daemon,
+                _ ("The 'userhash' and 'charset' ('prefer_utf8') parameters " \
+                   "are not compatible with RFC2069 and igored.\n"));
+#endif
+    userhash_support = 0;
+    prefer_utf8 = 0;
+  }
+
   if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
   {
 #ifdef HAVE_MESSAGES
@@ -2858,9 +2946,6 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
     return MHD_NO;
   }
 
-  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
-    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
-
   /* Calculate required size */
   buf_size = 0;
   /* 'Digest ' */
@@ -2875,8 +2960,11 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
     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_);
+  if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop)
+  {
+    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)
@@ -2947,15 +3035,18 @@ MHD_queue_auth_required_response3 (struct 
MHD_Connection *connection,
   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++] = ' ';
+  if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop)
+  {
+    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));
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 6906e1bb..fa243a34 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -2148,6 +2148,11 @@ struct MHD_Daemon
    */
   const char *digest_auth_random;
 
+  /**
+   * The malloc'ed copy of the @a digest_auth_random.
+   */
+  void *digest_auth_random_copy;
+
   /**
    * An array that contains the map nonce-nc.
    */
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index 26e59bfc..c06787a1 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -158,7 +158,18 @@ core
 /test_digestauth_emu_ext
 /test_digestauth_emu_ext_oldapi
 /test_digestauth2
-/test_digestauth2_oldapi
+/test_digestauth2_rfc2069
+/test_digestauth2_rfc2069_userdigest
+/test_digestauth2_oldapi1
+/test_digestauth2_oldapi2
 /test_digestauth2_userhash
 /test_digestauth2_sha256
 /test_digestauth2_sha256_userhash
+/test_digestauth2_oldapi2_sha256
+/test_digestauth2_userdigest
+/test_digestauth2_oldapi1_userdigest
+/test_digestauth2_oldapi2_userdigest
+/test_digestauth2_userhash_userdigest
+/test_digestauth2_sha256_userdigest
+/test_digestauth2_oldapi2_sha256_userdigest
+/test_digestauth2_sha256_userhash_userdigest
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index e8297b05..34590e8f 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -171,10 +171,21 @@ check_PROGRAMS += \
   test_digestauth_emu_ext \
   test_digestauth_emu_ext_oldapi \
   test_digestauth2 \
-  test_digestauth2_oldapi \
+  test_digestauth2_rfc2069 \
+  test_digestauth2_rfc2069_userdigest \
+  test_digestauth2_oldapi1 \
+  test_digestauth2_oldapi2 \
   test_digestauth2_userhash \
   test_digestauth2_sha256 \
-  test_digestauth2_sha256_userhash
+  test_digestauth2_sha256_userhash \
+  test_digestauth2_oldapi2_sha256 \
+  test_digestauth2_userdigest \
+  test_digestauth2_oldapi1_userdigest \
+  test_digestauth2_oldapi2_userdigest \
+  test_digestauth2_userhash_userdigest \
+  test_digestauth2_sha256_userdigest \
+  test_digestauth2_oldapi2_sha256_userdigest \
+  test_digestauth2_sha256_userhash_userdigest
 endif
 
 if HEAVY_TESTS
@@ -287,7 +298,16 @@ test_digestauth_emu_ext_oldapi_SOURCES = \
 test_digestauth2_SOURCES = \
   test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
 
-test_digestauth2_oldapi_SOURCES = \
+test_digestauth2_rfc2069_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_rfc2069_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi1_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi2_SOURCES = \
   test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
 
 test_digestauth2_userhash_SOURCES = \
@@ -296,9 +316,33 @@ test_digestauth2_userhash_SOURCES = \
 test_digestauth2_sha256_SOURCES = \
   test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
 
+test_digestauth2_oldapi2_sha256_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
 test_digestauth2_sha256_userhash_SOURCES = \
   test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
 
+test_digestauth2_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi1_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi2_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_userhash_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_sha256_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi2_sha256_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_sha256_userhash_userdigest_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
 test_get_iovec_SOURCES = \
   test_get_iovec.c mhd_has_in_name.h
 
diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c
index c00dd1b1..6054af7d 100644
--- a/src/testcurl/test_digestauth2.c
+++ b/src/testcurl/test_digestauth2.c
@@ -229,7 +229,7 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 
 #define MHD_URI_BASE_PATH "/bar%20foo?key=value"
 
-#define REALM "TestRealm"
+#define REALM_VAL "TestRealm"
 #define USERNAME1 "test_user"
 /* The hex form of MD5("test_user:TestRealm") */
 #define USERHASH1_MD5_HEX "c53c601503ff176f18f623725fba4281"
@@ -242,6 +242,15 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 #define USERHASH1_SHA256_BIN 0x09, 0x0c, 0x7e, 0x06, 0xb7, 0x7d, 0x66, 0x14, \
   0xcf, 0x5f, 0xe6, 0xca, 0xfa, 0x00, 0x4d, 0x2e, 0x5f, 0x8f, 0xb3, 0x6b, \
   0xa4, 0x5a, 0x0e, 0x35, 0xea, 0xcb, 0x2e, 0xb7, 0x72, 0x8f, 0x34, 0xde
+/* The hex form of MD5("test_user:TestRealm:test pass") */
+#define USERDIGEST1_MD5_BIN 0xd8, 0xb4, 0xa6, 0xd0, 0x01, 0x13, 0x07, 0xb7, \
+  0x67, 0x94, 0xea, 0x66, 0x86, 0x03, 0x6b, 0x43
+/* The hex form of SHA-256("test_user:TestRealm:test pass") */
+/* The binary form of SHA-256("test_user:TestRealm:test pass") */
+#define USERDIGEST1_SHA256_BIN 0xc3, 0x4e, 0x16, 0x5a, 0x17, 0x0f, 0xe5, \
+  0xac, 0x04, 0xf1, 0x6e, 0x46, 0x48, 0x2b, 0xa0, 0xc6, 0x56, 0xc1, 0xfb, \
+  0x8f, 0x66, 0xa6, 0xd6, 0x3f, 0x91, 0x12, 0xf8, 0x56, 0xa5, 0xec, 0x6d, \
+  0x6d
 /* "titkos szuperügynök" in UTF-8 */
 #define USERNAME2 "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k"
 /* percent-encoded username */
@@ -262,7 +271,9 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 static int verbose;
 static int test_oldapi;
 static int test_userhash;
+static int test_userdigest;
 static int test_sha256;
+static int test_rfc2069;
 static int curl_uses_usehash;
 
 /* Static helper variables */
@@ -270,9 +281,13 @@ static const char userhash1_md5_hex[] = USERHASH1_MD5_HEX;
 static const uint8_t userhash1_md5_bin[] = { USERHASH1_MD5_BIN };
 static const char userhash1_sha256_hex[] = USERHASH1_SHA256_HEX;
 static const uint8_t userhash1_sha256_bin[] = { USERHASH1_SHA256_BIN };
-static const char *userhash1_hex;
-static size_t userhash1_hex_len;
-static const uint8_t *userhash1_bin;
+static const char *userhash_hex;
+static size_t userhash_hex_len;
+static const uint8_t *userhash_bin;
+static const uint8_t userdigest1_md5_bin[] = { USERDIGEST1_MD5_BIN };
+static const uint8_t userdigest1_sha256_bin[] = { USERDIGEST1_SHA256_BIN };
+static const uint8_t *userdigest_bin;
+static size_t userdigest_bin_size;
 static const char *username_ptr;
 
 static void
@@ -286,22 +301,28 @@ test_global_init (void)
   username_ptr = USERNAME1;
   if (! test_sha256)
   {
-    userhash1_hex = userhash1_md5_hex;
-    userhash1_hex_len = MHD_STATICSTR_LEN_ (userhash1_md5_hex);
-    userhash1_bin = userhash1_md5_bin;
-    if ((userhash1_hex_len / 2) != \
+    userhash_hex = userhash1_md5_hex;
+    userhash_hex_len = MHD_STATICSTR_LEN_ (userhash1_md5_hex);
+    userhash_bin = userhash1_md5_bin;
+    if ((userhash_hex_len / 2) != \
         (sizeof(userhash1_md5_bin) / sizeof(userhash1_md5_bin[0])))
       externalErrorExitDesc ("Wrong size of the 'userhash1_md5_bin' array");
+    userdigest_bin = userdigest1_md5_bin;
+    userdigest_bin_size =
+      (sizeof(userdigest1_md5_bin) / sizeof(userdigest1_md5_bin[0]));
   }
   else
   {
-    userhash1_hex = userhash1_sha256_hex;
-    userhash1_hex_len = MHD_STATICSTR_LEN_ (userhash1_sha256_hex);
-    userhash1_bin = userhash1_sha256_bin;
-    if ((userhash1_hex_len / 2) != \
+    userhash_hex = userhash1_sha256_hex;
+    userhash_hex_len = MHD_STATICSTR_LEN_ (userhash1_sha256_hex);
+    userhash_bin = userhash1_sha256_bin;
+    if ((userhash_hex_len / 2) != \
         (sizeof(userhash1_sha256_bin)   \
          / sizeof(userhash1_sha256_bin[0])))
       externalErrorExitDesc ("Wrong size of the 'userhash1_sha256_bin' array");
+    userdigest_bin = userdigest1_sha256_bin;
+    userdigest_bin_size =
+      (sizeof(userdigest1_sha256_bin) / sizeof(userdigest1_sha256_bin[0]));
   }
 }
 
@@ -417,6 +438,8 @@ ahc_echo (void *cls,
     struct MHD_DigestAuthInfo *dinfo;
     const enum MHD_DigestAuthAlgo3 algo3 =
       test_sha256 ? MHD_DIGEST_AUTH_ALGO3_SHA256 : MHD_DIGEST_AUTH_ALGO3_MD5;
+    const enum MHD_DigestAuthQOP qop =
+      test_rfc2069 ? MHD_DIGEST_AUTH_QOP_NONE : MHD_DIGEST_AUTH_QOP_AUTH;
 
     dinfo = MHD_digest_auth_get_request_info3 (connection);
     if (NULL != dinfo)
@@ -437,27 +460,27 @@ ahc_echo (void *cls,
                    (int) dinfo->uname_type);
           mhdErrorExitDesc ("Wrong 'uname_type'");
         }
-        else if (dinfo->username_len != userhash1_hex_len)
+        else if (dinfo->username_len != userhash_hex_len)
         {
           fprintf (stderr, "'username_len' does not match.\n"
                    "Expected: %u\tRecieved: %u. ",
-                   (unsigned) userhash1_hex_len,
+                   (unsigned) userhash_hex_len,
                    (unsigned) dinfo->username_len);
           mhdErrorExitDesc ("Wrong 'username_len'");
         }
-        else if (0 != memcmp (dinfo->username, userhash1_hex,
+        else if (0 != memcmp (dinfo->username, userhash_hex,
                               dinfo->username_len))
         {
           fprintf (stderr, "'username' does not match.\n"
                    "Expected: '%s'\tRecieved: '%.*s'. ",
-                   userhash1_hex,
+                   userhash_hex,
                    (int) dinfo->username_len,
                    dinfo->username);
           mhdErrorExitDesc ("Wrong 'username'");
         }
         else if (NULL == dinfo->userhash_bin)
           mhdErrorExitDesc ("'userhash_bin' is NULL");
-        else if (0 != memcmp (dinfo->userhash_bin, userhash1_bin,
+        else if (0 != memcmp (dinfo->userhash_bin, userhash_bin,
                               dinfo->username_len / 2))
           mhdErrorExitDesc ("Wrong 'userhash_bin'");
       }
@@ -500,13 +523,25 @@ ahc_echo (void *cls,
                  (int) dinfo->algo);
         mhdErrorExitDesc ("Wrong 'algo'");
       }
-      else if (10 >= dinfo->cnonce_len)
+      if (! test_rfc2069)
       {
-        fprintf (stderr, "Unexpected small 'cnonce_len': %ld. ",
-                 (long) dinfo->cnonce_len);
-        mhdErrorExitDesc ("Wrong 'cnonce_len'");
+        if (10 >= dinfo->cnonce_len)
+        {
+          fprintf (stderr, "Unexpected small 'cnonce_len': %ld. ",
+                   (long) dinfo->cnonce_len);
+          mhdErrorExitDesc ("Wrong 'cnonce_len'");
+        }
+      }
+      else
+      {
+        if (0 != dinfo->cnonce_len)
+        {
+          fprintf (stderr, "'cnonce_len' is not zero: %ld. ",
+                   (long) dinfo->cnonce_len);
+          mhdErrorExitDesc ("Wrong 'cnonce_len'");
+        }
       }
-      else if (NULL == dinfo->opaque)
+      if (NULL == dinfo->opaque)
         mhdErrorExitDesc ("'opaque' is NULL");
       else if (dinfo->opaque_len != MHD_STATICSTR_LEN_ (OPAQUE_VALUE))
       {
@@ -525,25 +560,25 @@ ahc_echo (void *cls,
                  dinfo->opaque);
         mhdErrorExitDesc ("Wrong 'opaque'");
       }
-      else if (MHD_DIGEST_AUTH_QOP_AUTH != dinfo->qop)
+      else if (qop != dinfo->qop)
       {
         fprintf (stderr, "Unexpected 'qop'.\n"
                  "Expected: %d\tRecieved: %d. ",
-                 (int) MHD_DIGEST_AUTH_QOP_AUTH,
+                 (int) qop,
                  (int) dinfo->qop);
         mhdErrorExitDesc ("Wrong 'qop'");
       }
       else if (NULL == dinfo->realm)
         mhdErrorExitDesc ("'realm' is NULL");
-      else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM))
+      else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM_VAL))
       {
         fprintf (stderr, "'realm_len' does not match.\n"
                  "Expected: %u\tRecieved: %u. ",
-                 (unsigned) MHD_STATICSTR_LEN_ (REALM),
+                 (unsigned) MHD_STATICSTR_LEN_ (REALM_VAL),
                  (unsigned) dinfo->realm_len);
         mhdErrorExitDesc ("Wrong 'realm_len'");
       }
-      else if (0 != memcmp (dinfo->realm, REALM, dinfo->realm_len))
+      else if (0 != memcmp (dinfo->realm, REALM_VAL, dinfo->realm_len))
       {
         fprintf (stderr, "'realm' does not match.\n"
                  "Expected: '%s'\tRecieved: '%.*s'. ",
@@ -569,27 +604,27 @@ ahc_echo (void *cls,
                    (int) uname->uname_type);
           mhdErrorExitDesc ("Wrong 'uname_type'");
         }
-        else if (uname->username_len != userhash1_hex_len)
+        else if (uname->username_len != userhash_hex_len)
         {
           fprintf (stderr, "'username_len' does not match.\n"
                    "Expected: %u\tRecieved: %u. ",
-                   (unsigned) userhash1_hex_len,
+                   (unsigned) userhash_hex_len,
                    (unsigned) uname->username_len);
           mhdErrorExitDesc ("Wrong 'username_len'");
         }
-        else if (0 != memcmp (uname->username, userhash1_hex,
+        else if (0 != memcmp (uname->username, userhash_hex,
                               uname->username_len))
         {
           fprintf (stderr, "'username' does not match.\n"
                    "Expected: '%s'\tRecieved: '%.*s'. ",
-                   userhash1_hex,
+                   userhash_hex,
                    (int) uname->username_len,
                    uname->username);
           mhdErrorExitDesc ("Wrong 'username'");
         }
         else if (NULL == uname->userhash_bin)
           mhdErrorExitDesc ("'userhash_bin' is NULL");
-        else if (0 != memcmp (uname->userhash_bin, userhash1_bin,
+        else if (0 != memcmp (uname->userhash_bin, userhash_bin,
                               uname->username_len / 2))
           mhdErrorExitDesc ("Wrong 'userhash_bin'");
       }
@@ -626,11 +661,22 @@ ahc_echo (void *cls,
       }
       MHD_free (uname);
 
-      check_res =
-        MHD_digest_auth_check3 (connection, REALM, username_ptr,
-                                PASSWORD_VALUE, 50 * TIMEOUTS_VAL,
-                                0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
-                                (enum MHD_DigestAuthMultiAlgo3) algo3);
+      if (! test_userdigest)
+        check_res =
+          MHD_digest_auth_check3 (connection, REALM_VAL, username_ptr,
+                                  PASSWORD_VALUE,
+                                  50 * TIMEOUTS_VAL,
+                                  0,
+                                  (enum MHD_DigestAuthMultiQOP) qop,
+                                  (enum MHD_DigestAuthMultiAlgo3) algo3);
+      else
+        check_res =
+          MHD_digest_auth_check_digest3 (connection, REALM_VAL, username_ptr,
+                                         userdigest_bin, userdigest_bin_size,
+                                         50 * TIMEOUTS_VAL,
+                                         0,
+                                         (enum MHD_DigestAuthMultiQOP) qop,
+                                         (enum MHD_DigestAuthMultiAlgo3) 
algo3);
 
       switch (check_res)
       {
@@ -641,38 +687,41 @@ ahc_echo (void *cls,
         break;
       /* Invalid results */
       case MHD_DAUTH_NONCE_STALE:
-        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+        mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
                           "MHD_DAUTH_NONCE_STALE");
         break;
       case MHD_DAUTH_NONCE_WRONG:
-        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+        mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
                           "MHD_DAUTH_NONCE_WRONG");
         break;
       case MHD_DAUTH_ERROR:
         externalErrorExitDesc ("General error returned " \
-                               "by 'MHD_digest_auth_check3()'");
+                               "by 'MHD_digest_auth_check[_digest]3()'");
         break;
       case MHD_DAUTH_WRONG_USERNAME:
-        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+        mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
                           "MHD_DAUTH_WRONG_USERNAME");
         break;
       case MHD_DAUTH_RESPONSE_WRONG:
-        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+        mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
                           "MHD_DAUTH_RESPONSE_WRONG");
         break;
       case MHD_DAUTH_WRONG_HEADER:
+        mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
+                          "MHD_DAUTH_WRONG_HEADER");
+        break;
       case MHD_DAUTH_WRONG_REALM:
       case MHD_DAUTH_WRONG_URI:
       case MHD_DAUTH_WRONG_QOP:
       case MHD_DAUTH_WRONG_ALGO:
       case MHD_DAUTH_TOO_LARGE:
-        fprintf (stderr, "'MHD_digest_auth_check3()' returned "
+        fprintf (stderr, "'MHD_digest_auth_check[_digest]3()' returned "
                  "unexpected result: %d. ",
                  check_res);
         mhdErrorExitDesc ("Wrong returned code");
         break;
       default:
-        fprintf (stderr, "'MHD_digest_auth_check3()' returned "
+        fprintf (stderr, "'MHD_digest_auth_check[_digest]3()' returned "
                  "impossible result code: %d. ",
                  check_res);
         mhdErrorExitDesc ("Impossible returned code");
@@ -697,18 +746,18 @@ ahc_echo (void *cls,
       if (NULL == response)
         mhdErrorExitDesc ("Response creation failed");
       res =
-        MHD_queue_auth_required_response3 (connection, REALM, OPAQUE_VALUE,
+        MHD_queue_auth_required_response3 (connection, REALM_VAL, OPAQUE_VALUE,
                                            "/", response, 0,
-                                           MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                           (enum MHD_DigestAuthMultiQOP) qop,
                                            (enum MHD_DigestAuthMultiAlgo3) 
algo3,
                                            test_userhash, 0);
       if (MHD_YES != res)
         mhdErrorExitDesc ("'MHD_queue_auth_required_response3()' failed");
     }
   }
-  else
+  else if (2 == test_oldapi)
   {
-    /* Use old API */
+    /* Use old API v2 */
     char *username;
     int check_res;
 
@@ -725,14 +774,27 @@ ahc_echo (void *cls,
       }
       MHD_free (username);
 
-      check_res = MHD_digest_auth_check (connection, REALM, username_ptr,
-                                         PASSWORD_VALUE, 50 * TIMEOUTS_VAL);
+      if (! test_userdigest)
+        check_res =
+          MHD_digest_auth_check2 (connection, REALM_VAL, username_ptr,
+                                  PASSWORD_VALUE,
+                                  50 * TIMEOUTS_VAL,
+                                  test_sha256 ?
+                                  MHD_DIGEST_ALG_SHA256 : MHD_DIGEST_ALG_MD5);
+      else
+        check_res =
+          MHD_digest_auth_check_digest2 (connection, REALM_VAL, username_ptr,
+                                         userdigest_bin, userdigest_bin_size,
+                                         50 * TIMEOUTS_VAL,
+                                         test_sha256 ?
+                                         MHD_DIGEST_ALG_SHA256 :
+                                         MHD_DIGEST_ALG_MD5);
 
       if (MHD_YES != check_res)
       {
-        fprintf (stderr, "'MHD_digest_auth_check()' returned unexpected"
-                 " result: %d. ", check_res);
-        mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check()' result");
+        fprintf (stderr, "'MHD_digest_auth_check[_digest]2()' returned "
+                 "unexpected result: %d. ", check_res);
+        mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check[_digest]2()' result");
       }
       response =
         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
@@ -753,12 +815,79 @@ ahc_echo (void *cls,
       if (NULL == response)
         mhdErrorExitDesc ("Response creation failed");
 
-      res = MHD_queue_auth_fail_response (connection, REALM, OPAQUE_VALUE,
+      res = MHD_queue_auth_fail_response2 (connection, REALM_VAL, OPAQUE_VALUE,
+                                           response, 0,
+                                           test_sha256 ?
+                                           MHD_DIGEST_ALG_SHA256 :
+                                           MHD_DIGEST_ALG_MD5);
+      if (MHD_YES != res)
+        mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
+    }
+  }
+  else if (1 == test_oldapi)
+  {
+    /* Use old API v1 */
+    char *username;
+    int check_res;
+
+    username = MHD_digest_auth_get_username (connection);
+    if (NULL != username)
+    { /* Has a valid username in header */
+      if (0 != strcmp (username, username_ptr))
+      {
+        fprintf (stderr, "'username' does not match.\n"
+                 "Expected: '%s'\tRecieved: '%s'. ",
+                 username_ptr,
+                 username);
+        mhdErrorExitDesc ("Wrong 'username'");
+      }
+      MHD_free (username);
+
+      if (! test_userdigest)
+        check_res =
+          MHD_digest_auth_check (connection, REALM_VAL, username_ptr,
+                                 PASSWORD_VALUE,
+                                 50 * TIMEOUTS_VAL);
+      else
+        check_res =
+          MHD_digest_auth_check_digest (connection, REALM_VAL, username_ptr,
+                                        userdigest_bin,
+                                        50 * TIMEOUTS_VAL);
+
+      if (MHD_YES != check_res)
+      {
+        fprintf (stderr, "'MHD_digest_auth_check[_digest]()' returned "
+                 "unexpected result: %d. ", check_res);
+        mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check[_digest]()' result");
+      }
+      response =
+        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
+                                                (const void *) PAGE);
+      if (NULL == response)
+        mhdErrorExitDesc ("Response creation failed");
+
+      if (MHD_YES !=
+          MHD_queue_response (connection, MHD_HTTP_OK, response))
+        mhdErrorExitDesc ("'MHD_queue_response()' failed");
+    }
+    else
+    {
+      /* Has no valid username in header */
+      response =
+        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
+                                                (const void *) DENIED);
+      if (NULL == response)
+        mhdErrorExitDesc ("Response creation failed");
+
+      res = MHD_queue_auth_fail_response (connection, REALM_VAL, OPAQUE_VALUE,
                                           response, 0);
       if (MHD_YES != res)
         mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
     }
   }
+  else
+    externalErrorExitDesc ("Wrong 'test_oldapi' value");
+
   MHD_destroy_response (response);
   return MHD_YES;
 }
@@ -979,31 +1108,35 @@ testDigestAuth (void)
 {
   struct MHD_Daemon *d;
   uint16_t port;
-  uint8_t salt[8];
   struct CBC cbc;
   char buf[2048];
   CURL *c;
   int failed = 0;
 
-  if (! gen_good_rnd (salt, sizeof(salt)))
-  {
-    fprintf (stderr, "WARNING: the random buffer (used as salt value) is not "
-             "initialised completely, nonce generation may be "
-             "predictable in this test.\n");
-    fflush (stderr);
-  }
-
   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
     port = 0;
   else
     port = 4210;
 
-  d = MHD_start_daemon (MHD_USE_ERROR_LOG,
-                        port, NULL, NULL,
-                        &ahc_echo, NULL,
-                        MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (salt), salt,
-                        MHD_OPTION_NONCE_NC_SIZE, 300,
-                        MHD_OPTION_END);
+  if (1)
+  {
+    uint8_t salt[8]; /* Use local variable to test MHD "copy" function */
+    if (! gen_good_rnd (salt, sizeof(salt)))
+    {
+      fprintf (stderr, "WARNING: the random buffer (used as salt value) is not 
"
+               "initialised completely, nonce generation may be "
+               "predictable in this test.\n");
+      fflush (stderr);
+    }
+
+    d = MHD_start_daemon (MHD_USE_ERROR_LOG,
+                          port, NULL, NULL,
+                          &ahc_echo, NULL,
+                          MHD_OPTION_DIGEST_AUTH_RANDOM_COPY,
+                          sizeof (salt), salt,
+                          MHD_OPTION_NONCE_NC_SIZE, 300,
+                          MHD_OPTION_END);
+  }
   if (d == NULL)
     return 1;
   if (0 == port)
@@ -1046,8 +1179,8 @@ main (int argc, char *const *argv)
 {
 #if ! CURL_AT_LEAST_VERSION (7,19,1)
   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
-  /* Need version 7.19.1 for separate username and password */
-  fprintf (stderr, "Required libcurl version 7.19.1 at least"
+  /* Need version 7.19.1 or newer for separate username and password */
+  fprintf (stderr, "Required libcurl at least version 7.19.1"
            " to run this test.\n");
   return 77;
 #else  /* CURL_AT_LEAST_VERSION(7,19,1) */
@@ -1062,13 +1195,30 @@ main (int argc, char *const *argv)
                has_param (argc, argv, "--quiet") ||
                has_param (argc, argv, "-s") ||
                has_param (argc, argv, "--silent"));
-  test_oldapi = has_in_name (argv[0], "_oldapi");
+  test_oldapi = 0;
+  if (has_in_name (argv[0], "_oldapi1"))
+    test_oldapi = 1;
+  if (has_in_name (argv[0], "_oldapi2"))
+    test_oldapi = 2;
   test_userhash = has_in_name (argv[0], "_userhash");
+  test_userdigest = has_in_name (argv[0], "_userdigest");
   test_sha256 = has_in_name (argv[0], "_sha256");
+  test_rfc2069 = has_in_name (argv[0], "_rfc2069");
 
+  /* Wrong test types combinations */
+  if (1 == test_oldapi)
+  {
+    if (test_sha256)
+      return 99;
+  }
   if (test_oldapi)
-  { /* Wrong test types combination */
-    if (test_userhash || test_sha256)
+  {
+    if (test_userhash || test_rfc2069)
+      return 99;
+  }
+  if (test_rfc2069)
+  {
+    if (test_userhash)
       return 99;
   }
 

-- 
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]