From 4833b58c484c4eb8b429887b472bf4967cf88320 Mon Sep 17 00:00:00 2001
From: Eric Covener <covener@apache.org>
Date: Sun, 26 Apr 2026 16:26:29 +0000
Subject: [PATCH] Merge r1933355 from trunk:

mod_auth_digest: use apr_crypto_equals



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1933356 13f79535-47bb-0310-9956-ffa450edef68
---
 configure.in                  |  4 ++--
 modules/aaa/mod_auth_digest.c | 45 ++++++++++++++---------------------
 2 files changed, 20 insertions(+), 29 deletions(-)

diff --git a/configure.in b/configure.in
index d2a009d7902..d49cb28465a 100644
--- a/configure.in
+++ b/configure.in
@@ -431,10 +431,10 @@ if test "${apu_found}" = "yes"; then
     # we need to add the APR includes to CPPFLAGS
     apu_ckver_CPPFLAGS="$CPPFLAGS"
     CPPFLAGS="$CPPFLAGS `$apr_config --includes`"
-    APACHE_CHECK_APxVER([apu], 1, 3)
+    APACHE_CHECK_APxVER([apu], 1, 6)
     CPPFLAGS="$apu_ckver_CPPFLAGS"
   else
-    APACHE_CHECK_APxVER([apu], 1, 3)
+    APACHE_CHECK_APxVER([apu], 1, 6)
   fi
 fi
 
diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c
index 791cec2bc7c..3ef5f256de0 100644
--- a/modules/aaa/mod_auth_digest.c
+++ b/modules/aaa/mod_auth_digest.c
@@ -73,6 +73,7 @@
 #include "apr_shm.h"
 #include "apr_rmm.h"
 #include "ap_provider.h"
+#include "apr_crypto.h" /* for apr_crypto_equals */
 
 #include "mod_auth.h"
 
@@ -100,10 +101,17 @@ typedef struct digest_config_struct {
 #define DFLT_NONCE_LIFE apr_time_from_sec(300)
 #define NEXTNONCE_DELTA apr_time_from_sec(30)
 
-
+/* The server nonce has fixed length and is the concatenation of:
+ *    base64(apr_time_t timestamp) + hex(SHA1(realm+time[+opaque])) */
 #define NONCE_TIME_LEN  (((sizeof(apr_time_t)+2)/3)*4)
 #define NONCE_HASH_LEN  (2*APR_SHA1_DIGESTSIZE)
 #define NONCE_LEN       (int )(NONCE_TIME_LEN + NONCE_HASH_LEN)
+/* Evaluates to true if nonce string is valid. Since the time part of
+ * the nonce is a base64 encoding of an apr_time_t (8 bytes), it
+ * must end with a '='.  */
+#define VALID_NONCE(n_) ((n_) && strlen((n_)) == NONCE_LEN && (n_)[NONCE_TIME_LEN - 1] == '=')
+
+#define MD5_DIGEST_LEN (2*APR_MD5_DIGESTSIZE) /* ignoring trailing \0 */
 
 #define SECRET_LEN          20
 #define RETAINED_DATA_ID    "mod_auth_digest"
@@ -1017,8 +1025,9 @@ static int get_digest_rec(request_rec *r, digest_header_rec *resp)
             resp->nonce_count = apr_pstrdup(r->pool, value);
     }
 
-    if (!resp->username || !resp->realm || !resp->nonce || !resp->uri
-        || !resp->digest
+    if (!resp->username || !resp->realm || !resp->uri
+        || !VALID_NONCE(resp->nonce)
+        || !resp->digest || strlen(resp->digest) != MD5_DIGEST_LEN
         || (resp->message_qop && (!resp->cnonce || !resp->nonce_count))) {
         resp->auth_hdr_sts = INVALID;
         return !OK;
@@ -1070,14 +1079,9 @@ static int parse_hdr_and_update_nc(request_rec *r)
 }
 
 
-/*
- * Nonce generation code
- */
-
-/* The hash part of the nonce is a SHA-1 hash of the time, realm, server host
- * and port, opaque, and our secret.
- */
-static void gen_nonce_hash(char *hash, const char *timestr, const char *opaque,
+/* Writes the hash part of the server nonce to hash, which must be of
+ * minimum size (NONCE_HASH_LEN+1). */
+static void gen_nonce_hash(char hash[NONCE_HASH_LEN+1], const char *timestr, const char *opaque,
                            const server_rec *server,
                            const digest_config_rec *conf)
 {
@@ -1426,19 +1430,6 @@ static int check_nonce(request_rec *r, digest_header_rec *resp,
     time_rec nonce_time;
     char tmp, hash[NONCE_HASH_LEN+1];
 
-    /* Since the time part of the nonce is a base64 encoding of an
-     * apr_time_t (8 bytes), it should end with a '=', fail early otherwise.
-     */
-    if (strlen(resp->nonce) != NONCE_LEN
-            || resp->nonce[NONCE_TIME_LEN - 1] != '=') {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01775)
-                      "invalid nonce '%s' received - length is not %d "
-                      "or time encoding is incorrect",
-                      resp->nonce, NONCE_LEN);
-        note_digest_auth_failure(r, conf, resp, 1);
-        return HTTP_UNAUTHORIZED;
-    }
-
     tmp = resp->nonce[NONCE_TIME_LEN];
     resp->nonce[NONCE_TIME_LEN] = '\0';
     apr_base64_decode_binary(nonce_time.arr, resp->nonce);
@@ -1446,7 +1437,7 @@ static int check_nonce(request_rec *r, digest_header_rec *resp,
     resp->nonce[NONCE_TIME_LEN] = tmp;
     resp->nonce_time = nonce_time.time;
 
-    if (strcmp(hash, resp->nonce+NONCE_TIME_LEN)) {
+    if (!apr_crypto_equals(hash, resp->nonce+NONCE_TIME_LEN, NONCE_HASH_LEN)) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01776)
                       "invalid nonce %s received - hash is not %s",
                       resp->nonce, hash);
@@ -1796,7 +1787,7 @@ static int authenticate_digest_user(request_rec *r)
 
     if (resp->message_qop == NULL) {
         /* old (rfc-2069) style digest */
-        if (strcmp(resp->digest, old_digest(r, resp))) {
+        if (!apr_crypto_equals(resp->digest, old_digest(r, resp), MD5_DIGEST_LEN)) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01792)
                           "user %s: password mismatch: %s", r->user,
                           r->uri);
@@ -1831,7 +1822,7 @@ static int authenticate_digest_user(request_rec *r)
             /* we failed to allocate a client struct */
             return HTTP_INTERNAL_SERVER_ERROR;
         }
-        if (strcmp(resp->digest, exp_digest)) {
+        if (!apr_crypto_equals(resp->digest, exp_digest, MD5_DIGEST_LEN)) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01794)
                           "user %s: password mismatch: %s", r->user,
                           r->uri);
