Fix Nonce replay vulnerability in Digest authentication
fixed in version 3.5.2 (CVE-2014-9749, bsc#949942)

=== modified file 'src/auth/digest/Config.cc'
Index: src/auth/digest/auth_digest.cc
===================================================================
--- src/auth/digest/auth_digest.cc.orig
+++ src/auth/digest/auth_digest.cc
@@ -475,47 +475,49 @@ authDigestNoncePurge(digest_nonce_h * no
         return;
 
     hash_remove_link(digest_nonce_cache, nonce);
 
     nonce->flags.incache = 0;
 
     /* the cache's link */
     authDigestNonceUnlink(nonce);
 }
 
 /* USER related functions */
 static AuthUser *
 authDigestUserFindUsername(const char *username)
 {
     AuthUserHashPointer *usernamehash;
-    AuthUser *auth_user;
     debugs(29, 9, HERE << "Looking for user '" << username << "'");
 
     if (username && (usernamehash = static_cast < AuthUserHashPointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
-        while ((usernamehash->user()->auth_type != AUTH_DIGEST) &&
+        while (((usernamehash->user()->auth_type != AUTH_DIGEST) ||
+                (strcmp(username, usernamehash->user()->username()) != 0) ||
+                 usernamehash->user()->expiretime + ::Config.authenticateTTL < current_time.tv_sec) &&
                 (usernamehash->next))
             usernamehash = static_cast < AuthUserHashPointer * >(usernamehash->next);
 
-        auth_user = NULL;
+        if (usernamehash->user()->auth_type == AUTH_DIGEST &&
+            strcmp(username, usernamehash->user()->username()) == 0 &&
+            usernamehash->user()->expiretime + ::Config.authenticateTTL > current_time.tv_sec) {
 
-        if (usernamehash->user()->auth_type == AUTH_DIGEST) {
-            auth_user = usernamehash->user();
+            debugs(29, 9, HERE << "User found in cache: " << username);
+            return usernamehash->user();
         }
-
-        return auth_user;
     }
 
+    debugs(29, 9, HERE << "User not found in cache: " << username);
     return NULL;
 }
 
 static void
 authDigestUserShutdown(void)
 {
     /** \todo Future work: the auth framework could flush it's cache */
     AuthUserHashPointer *usernamehash;
     AuthUser *auth_user;
     hash_first(proxy_auth_username_cache);
 
     while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
         auth_user = usernamehash->user();
 
         if (strcmp(auth_user->config->type(), "digest") == 0)
@@ -703,34 +705,30 @@ AuthDigestUserRequest::authenticate(Http
         }
 
         /* check for stale nonce */
         if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
             debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale");
             credentials(Failed);
             digest_request->setDenyMessage("Stale nonce");
             return;
         }
     }
 
     credentials(Ok);
 
     /* password was checked and did match */
     debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK");
-
-    /* auth_user is now linked, we reset these values
-     * after external auth occurs anyway */
-    auth_user->expiretime = current_time.tv_sec;
     return;
 }
 
 int
 AuthDigestUserRequest::module_direction()
 {
     switch (credentials()) {
 
     case Unchecked:
         return -1;
 
     case Ok:
 
         return 0;
 
@@ -1368,30 +1366,34 @@ AuthDigestConfig::decode(char const *pro
         /* save the username */
         digest_user->username(username);
         /* set the user type */
         digest_user->auth_type = AUTH_DIGEST;
         /* this auth_user struct is the one to get added to the
          * username cache */
         /* store user in hash's */
         digest_user->addToNameCache();
 
         /*
          * Add the digest to the user so we can tell if a hacking
          * or spoofing attack is taking place. We do this by assuming
          * the user agent won't change user name without warning.
          */
         authDigestUserLinkNonce(digest_user, nonce);
+
+        /* auth_user is now linked, we reset these values
+         * after external auth occurs anyway */
+        auth_user->expiretime = current_time.tv_sec;
     } else {
         debugs(29, 9, "authDigestDecodeAuth: Found user '" << username << "' in the user cache as '" << auth_user << "'");
         digest_user = static_cast < digest_user_h * >(auth_user);
         xfree(username);
     }
 
     /*link the request and the user */
     assert(digest_request != NULL);
 
     digest_request->user(digest_user);
 
     digest_user->addRequest (digest_request);
 
     debugs(29, 9, "username = '" << digest_user->username() << "'\nrealm = '" <<
            digest_request->realm << "'\nqop = '" << digest_request->qop <<
