You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by sf...@apache.org on 2013/01/01 21:16:31 UTC

svn commit: r1427548 - in /httpd/httpd/trunk: CHANGES include/ap_mmn.h include/httpd.h modules/aaa/mod_authn_dbd.c modules/aaa/mod_authn_dbm.c modules/aaa/mod_authn_file.c modules/aaa/mod_authn_socache.c server/util.c

Author: sf
Date: Tue Jan  1 20:16:30 2013
New Revision: 1427548

URL: http://svn.apache.org/viewvc?rev=1427548&view=rev
Log:
Add some caching for password hash validation.

Password hash functions must be expensive in order to be secure. But
if they have to be re-evaluated for every request, performance
suffers.

As a minimal remedy, cache the most recent result for every
connection. This gives a great performance boost if a web browser
does many requests on the same connection with the same
user+password.  In principle, this may keep the plain text password
around longer than before. But in practice, there won't be much
difference since user+password can already remain in some unused
data bucket for longer than the request duration.

A proper solution still needs to be found for connections from
proxies which may carry requests for many different users.

While it currently only requires the conn_rec, the new
ap_password_validate() function takes username and request_rec to
allow future extensions, like detection of brute-force attempts.


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/include/ap_mmn.h
    httpd/httpd/trunk/include/httpd.h
    httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c
    httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c
    httpd/httpd/trunk/modules/aaa/mod_authn_file.c
    httpd/httpd/trunk/modules/aaa/mod_authn_socache.c
    httpd/httpd/trunk/server/util.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Tue Jan  1 20:16:30 2013
@@ -1,6 +1,11 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_authn_file, mod_authn_dbd, mod_authn_dbm, mod_authn_socache:
+     Cache the result of the most recent password hash verification for every
+     keep-alive connection. This saves some expensive calculations.
+     [Stefan Fritsch]
+
   *) http: Remove support for Request-Range header sent by Navigator 2-3 and
      MSIE 3. [Stefan Fritsch]
 

Modified: httpd/httpd/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Tue Jan  1 20:16:30 2013
@@ -412,6 +412,7 @@
  *                         core_server_config again, add http09_enable
  * 20121222.1 (2.5.0-dev)  Add http_conformance to core_server_config,
  *                         add ap_has_cntrl()
+ * 20121222.2 (2.5.0-dev)  Add ap_password_validate()
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
@@ -419,7 +420,7 @@
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20121222
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 1                   /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 2                   /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

Modified: httpd/httpd/trunk/include/httpd.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/httpd.h?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/include/httpd.h (original)
+++ httpd/httpd/trunk/include/httpd.h Tue Jan  1 20:16:30 2013
@@ -2274,6 +2274,24 @@ AP_DECLARE(void) ap_bin2hex(const void *
 AP_DECLARE(int) ap_has_cntrl(const char *str)
                 AP_FN_ATTR_NONNULL_ALL;
 
+/**
+ * Wrapper for @a apr_password_validate() to cache expensive calculations
+ * @param r the current request
+ * @param username username of the user
+ * @param passwd password string
+ * @param hash hash string to be passwd to @a apr_password_validate()
+ * @return APR_SUCCESS if passwords match, APR_EMISMATCH or error otherwise
+ * @note Currently, ap_password_validate() only caches the result of the
+ *       most recent call with the same connection as @a r.
+ *       In the future, it may also do rate-limiting against brute-force
+ *       attacks.
+ */
+AP_DECLARE(apr_status_t) ap_password_validate(request_rec *r,
+                                              const char *username,
+                                              const char *passwd,
+                                              const char *hash);
+
+
 #define AP_NORESTART APR_OS_START_USEERR + 1
 
 #ifdef __cplusplus

Modified: httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c (original)
+++ httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c Tue Jan  1 20:16:30 2013
@@ -179,7 +179,7 @@ static authn_status authn_dbd_password(r
     }
     AUTHN_CACHE_STORE(r, user, NULL, dbd_password);
 
-    rv = apr_password_validate(password, dbd_password);
+    rv = ap_password_validate(r, user, password, dbd_password);
 
     if (rv != APR_SUCCESS) {
         return AUTH_DENIED;

Modified: httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c (original)
+++ httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c Tue Jan  1 20:16:30 2013
@@ -27,7 +27,6 @@
 #include "apr_want.h"
 #include "apr_strings.h"
 #include "apr_dbm.h"
-#include "apr_md5.h"        /* for apr_password_validate */
 
 #include "ap_provider.h"
 #include "httpd.h"
@@ -144,7 +143,7 @@ static authn_status check_dbm_pw(request
     }
     AUTHN_CACHE_STORE(r, user, NULL, dbm_password);
 
-    rv = apr_password_validate(password, dbm_password);
+    rv = ap_password_validate(r, user, password, dbm_password);
 
     if (rv != APR_SUCCESS) {
         return AUTH_DENIED;

Modified: httpd/httpd/trunk/modules/aaa/mod_authn_file.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_file.c?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/aaa/mod_authn_file.c (original)
+++ httpd/httpd/trunk/modules/aaa/mod_authn_file.c Tue Jan  1 20:16:30 2013
@@ -15,7 +15,6 @@
  */
 
 #include "apr_strings.h"
-#include "apr_md5.h"            /* for apr_password_validate */
 
 #include "ap_config.h"
 #include "ap_provider.h"
@@ -112,7 +111,7 @@ static authn_status check_password(reque
     }
     AUTHN_CACHE_STORE(r, user, NULL, file_password);
 
-    status = apr_password_validate(password, file_password);
+    status = ap_password_validate(r, user, password, file_password);
     if (status != APR_SUCCESS) {
         return AUTH_DENIED;
     }

Modified: httpd/httpd/trunk/modules/aaa/mod_authn_socache.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_socache.c?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/aaa/mod_authn_socache.c (original)
+++ httpd/httpd/trunk/modules/aaa/mod_authn_socache.c Tue Jan  1 20:16:30 2013
@@ -15,7 +15,6 @@
  */
 
 #include "apr_strings.h"
-#include "apr_md5.h"            /* for apr_password_validate */
 
 #include "ap_config.h"
 #include "ap_provider.h"
@@ -375,7 +374,7 @@ static authn_status check_password(reque
         return AUTH_USER_NOT_FOUND;
     }
 
-    rv = apr_password_validate(password, (char*) val);
+    rv = ap_password_validate(r, user, password, (char*) val);
     if (rv != APR_SUCCESS) {
         return AUTH_DENIED;
     }

Modified: httpd/httpd/trunk/server/util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/util.c?rev=1427548&r1=1427547&r2=1427548&view=diff
==============================================================================
--- httpd/httpd/trunk/server/util.c (original)
+++ httpd/httpd/trunk/server/util.c Tue Jan  1 20:16:30 2013
@@ -30,6 +30,7 @@
 #include "apr.h"
 #include "apr_strings.h"
 #include "apr_lib.h"
+#include "apr_md5.h"            /* for apr_password_validate */
 
 #define APR_WANT_STDIO
 #define APR_WANT_STRFUNC
@@ -2896,3 +2897,42 @@ AP_DECLARE(void) ap_get_loadavg(ap_loada
     }
 #endif
 }
+
+static const char * const pw_cache_note_name = "conn_cache_note";
+struct pw_cache {
+    /* varbuf contains concatenated password and hash */
+    struct ap_varbuf vb;
+    apr_size_t pwlen;
+    apr_status_t result;
+};
+
+AP_DECLARE(apr_status_t) ap_password_validate(request_rec *r,
+                                              const char *username,
+                                              const char *passwd,
+                                              const char *hash)
+{
+    struct pw_cache *cache;
+    apr_size_t hashlen;
+
+    cache = (struct pw_cache *)apr_table_get(r->connection->notes, pw_cache_note_name);
+    if (cache != NULL) {
+        if (strncmp(passwd, cache->vb.buf, cache->pwlen) == 0
+            && strcmp(hash, cache->vb.buf + cache->pwlen) == 0) {
+            return cache->result;
+        }
+        /* make ap_varbuf_grow below not copy the old data */
+        cache->vb.strlen = 0;
+    }
+    else {
+        cache = apr_palloc(r->connection->pool, sizeof(struct pw_cache));
+        ap_varbuf_init(r->connection->pool, &cache->vb, 0);
+        apr_table_setn(r->connection->notes, pw_cache_note_name, (void *)cache);
+    }
+    cache->pwlen = strlen(passwd);
+    hashlen = strlen(hash);
+    ap_varbuf_grow(&cache->vb, cache->pwlen + hashlen + 1);
+    memcpy(cache->vb.buf, passwd, cache->pwlen);
+    memcpy(cache->vb.buf + cache->pwlen, hash, hashlen + 1);
+    cache->result = apr_password_validate(passwd, hash);
+    return cache->result;
+}



Re: Password caching (was: svn commit: r1427548)

Posted by Stefan Fritsch <sf...@sfritsch.de>.
On Saturday 05 January 2013, Igor Galić wrote:
> > No, mod_authn_socache only caches the lookup of the password
> > hash. It avoids having to open the password file/dbm/whatever
> > but it still calls apr_password_validate() every time. Maybe it
> > should be extended to also cache the real password and the
> > result of
> > apr_password_validate()?
> >
> > 
> 
> Stupid question time:
> Why can't we store the password hash in the socache instead of
> the plain-text password?

Because validating the password from the hash is slow. It has to be 
slow, in order to make it impossible to brute-force the password from 
the hash using today's graphics chips.

A single cpu core of a core i7 @ 2.8Ghz can do this many password 
validations per second:

     crypt:  4157 (I have been told that this could be improved by
                   reusing the struct crypt_data)
  md5crypt:  3552 (the current default algorithm)
   bcrypt5:   503 (cost setting 5, current default in htpasswd for
                   bcrypt)
   bcrypt8:    66 (cost setting 8, a common value for use
                   of bcrypt in /etc/passwd)

If the validation has to be done once per request, it severely limits 
the web server's performance.

Of course with form based auth, this is much less of a problem than 
with basic auth, because the password has only to be validated during 
login. But I would still like to have a viable and secure solution for 
basic auth.

Re: Password caching

Posted by Issac Goldstand <ma...@beamartyr.net>.
On 05/01/2013 11:52, Igor Galić wrote:
>
>
> ----- Original Message -----
>> On Wednesday 02 January 2013, Eric Covener wrote:
>>> On Wed, Jan 2, 2013 at 4:02 PM, Stefan Fritsch <sf...@sfritsch.de>
>> wrote:
>>>> On Wednesday 02 January 2013, Jim Jagielski wrote:
>>>>> For *real* improvement, wouldn't storing in socache be
>>>>> the optimal method?
>>>>
>>>> Yes. I fear there may be some knee-jerk reaction like "oh my god,
>>>> they are keeping all the passwords in plain-text". But if it
>>>> would be limited to the shmcb socache provider, and if the
>>>> passwords would be cleared after some time of not being used, I
>>>> don't see any real security problems. Any other opinions?
>>>
>>> For authentication, can you already opt-in to effectively this with
>>> the mod_authn_socache?
>>
>> No, mod_authn_socache only caches the lookup of the password hash. It
>> avoids having to open the password file/dbm/whatever but it still
>> calls apr_password_validate() every time. Maybe it should be extended
>> to also cache the real password and the result of
>> apr_password_validate()?
>>
>
> Stupid question time:
> Why can't we store the password *hash* in the socache instead of
> the plain-text password?
>
> i
>

Igor, that is exactly what Stefan is says already happens with 
mod_authn_socache, unless I grossly misread...

I'd be +1 allowing for a directive to hash the plaintext in socache, if 
it wasn't the default.  I'd probably be +0 to it being the default, but 
only because I don't see myself as having tuits to look closely enough 
at shmcb cache to really understand the security implications for 
myself; but if the sysadmin is already opt-ing in for any sort of 
authentication caching, then (s)he already is aware of a hypothetical 
chance for a security compromise on the system to some degree or another.

   Issac



Re: Password caching (was: svn commit: r1427548)

Posted by Igor Galić <i....@brainsware.org>.

----- Original Message -----
> On Wednesday 02 January 2013, Eric Covener wrote:
> > On Wed, Jan 2, 2013 at 4:02 PM, Stefan Fritsch <sf...@sfritsch.de>
> wrote:
> > > On Wednesday 02 January 2013, Jim Jagielski wrote:
> > >> For *real* improvement, wouldn't storing in socache be
> > >> the optimal method?
> > > 
> > > Yes. I fear there may be some knee-jerk reaction like "oh my god,
> > > they are keeping all the passwords in plain-text". But if it
> > > would be limited to the shmcb socache provider, and if the
> > > passwords would be cleared after some time of not being used, I
> > > don't see any real security problems. Any other opinions?
> > 
> > For authentication, can you already opt-in to effectively this with
> > the mod_authn_socache?
> 
> No, mod_authn_socache only caches the lookup of the password hash. It
> avoids having to open the password file/dbm/whatever but it still
> calls apr_password_validate() every time. Maybe it should be extended
> to also cache the real password and the result of
> apr_password_validate()?
> 

Stupid question time:
Why can't we store the password *hash* in the socache instead of
the plain-text password?

i

-- 
Igor Galić

Tel: +43 (0) 664 886 22 883
Mail: i.galic@brainsware.org
URL: http://brainsware.org/
GPG: 6880 4155 74BD FD7C B515  2EA5 4B1D 9E08 A097 C9AE


Password caching (was: svn commit: r1427548)

Posted by Stefan Fritsch <sf...@sfritsch.de>.
On Wednesday 02 January 2013, Eric Covener wrote:
> On Wed, Jan 2, 2013 at 4:02 PM, Stefan Fritsch <sf...@sfritsch.de> 
wrote:
> > On Wednesday 02 January 2013, Jim Jagielski wrote:
> >> For *real* improvement, wouldn't storing in socache be
> >> the optimal method?
> > 
> > Yes. I fear there may be some knee-jerk reaction like "oh my god,
> > they are keeping all the passwords in plain-text". But if it
> > would be limited to the shmcb socache provider, and if the
> > passwords would be cleared after some time of not being used, I
> > don't see any real security problems. Any other opinions?
> 
> For authentication, can you already opt-in to effectively this with
> the mod_authn_socache?

No, mod_authn_socache only caches the lookup of the password hash. It 
avoids having to open the password file/dbm/whatever but it still 
calls apr_password_validate() every time. Maybe it should be extended 
to also cache the real password and the result of 
apr_password_validate()?

Re: svn commit: r1427548 - in /httpd/httpd/trunk: CHANGES include/ap_mmn.h include/httpd.h modules/aaa/mod_authn_dbd.c modules/aaa/mod_authn_dbm.c modules/aaa/mod_authn_file.c modules/aaa/mod_authn_socache.c server/util.c

Posted by Eric Covener <co...@gmail.com>.
On Wed, Jan 2, 2013 at 4:02 PM, Stefan Fritsch <sf...@sfritsch.de> wrote:
> On Wednesday 02 January 2013, Jim Jagielski wrote:
>> For *real* improvement, wouldn't storing in socache be
>> the optimal method?
>
> Yes. I fear there may be some knee-jerk reaction like "oh my god, they
> are keeping all the passwords in plain-text". But if it would be
> limited to the shmcb socache provider, and if the passwords would be
> cleared after some time of not being used, I don't see any real
> security problems. Any other opinions?
>

For authentication, can you already opt-in to effectively this with
the mod_authn_socache?

Re: svn commit: r1427548 - in /httpd/httpd/trunk: CHANGES include/ap_mmn.h include/httpd.h modules/aaa/mod_authn_dbd.c modules/aaa/mod_authn_dbm.c modules/aaa/mod_authn_file.c modules/aaa/mod_authn_socache.c server/util.c

Posted by Stefan Fritsch <sf...@sfritsch.de>.
On Wednesday 02 January 2013, Jim Jagielski wrote:
> For *real* improvement, wouldn't storing in socache be
> the optimal method?

Yes. I fear there may be some knee-jerk reaction like "oh my god, they 
are keeping all the passwords in plain-text". But if it would be 
limited to the shmcb socache provider, and if the passwords would be 
cleared after some time of not being used, I don't see any real 
security problems. Any other opinions?


> On Jan 1, 2013, at 3:16 PM, sf@apache.org wrote:
> > Author: sf
> > Date: Tue Jan  1 20:16:30 2013
> > New Revision: 1427548
> > 
> > URL: http://svn.apache.org/viewvc?rev=1427548&view=rev
> > Log:
> > Add some caching for password hash validation.
> > 
> > Password hash functions must be expensive in order to be secure.
> > But if they have to be re-evaluated for every request,
> > performance suffers.
> > 
> > As a minimal remedy, cache the most recent result for every
> > connection. This gives a great performance boost if a web browser
> > does many requests on the same connection with the same
> > user+password.  In principle, this may keep the plain text
> > password around longer than before. But in practice, there won't
> > be much difference since user+password can already remain in
> > some unused data bucket for longer than the request duration.
> > 
> > A proper solution still needs to be found for connections from
> > proxies which may carry requests for many different users.
> > 
> > While it currently only requires the conn_rec, the new
> > ap_password_validate() function takes username and request_rec to
> > allow future extensions, like detection of brute-force attempts.

Re: svn commit: r1427548 - in /httpd/httpd/trunk: CHANGES include/ap_mmn.h include/httpd.h modules/aaa/mod_authn_dbd.c modules/aaa/mod_authn_dbm.c modules/aaa/mod_authn_file.c modules/aaa/mod_authn_socache.c server/util.c

Posted by Jim Jagielski <ji...@jaguNET.com>.
For *real* improvement, wouldn't storing in socache be
the optimal method?

On Jan 1, 2013, at 3:16 PM, sf@apache.org wrote:

> Author: sf
> Date: Tue Jan  1 20:16:30 2013
> New Revision: 1427548
> 
> URL: http://svn.apache.org/viewvc?rev=1427548&view=rev
> Log:
> Add some caching for password hash validation.
> 
> Password hash functions must be expensive in order to be secure. But
> if they have to be re-evaluated for every request, performance
> suffers.
> 
> As a minimal remedy, cache the most recent result for every
> connection. This gives a great performance boost if a web browser
> does many requests on the same connection with the same
> user+password.  In principle, this may keep the plain text password
> around longer than before. But in practice, there won't be much
> difference since user+password can already remain in some unused
> data bucket for longer than the request duration.
> 
> A proper solution still needs to be found for connections from
> proxies which may carry requests for many different users.
> 
> While it currently only requires the conn_rec, the new
> ap_password_validate() function takes username and request_rec to
> allow future extensions, like detection of brute-force attempts.
> 
> 
> Modified:
>    httpd/httpd/trunk/CHANGES
>    httpd/httpd/trunk/include/ap_mmn.h
>    httpd/httpd/trunk/include/httpd.h
>    httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c
>    httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c
>    httpd/httpd/trunk/modules/aaa/mod_authn_file.c
>    httpd/httpd/trunk/modules/aaa/mod_authn_socache.c
>    httpd/httpd/trunk/server/util.c
> 
> Modified: httpd/httpd/trunk/CHANGES
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/CHANGES [utf-8] (original)
> +++ httpd/httpd/trunk/CHANGES [utf-8] Tue Jan  1 20:16:30 2013
> @@ -1,6 +1,11 @@
>                                                          -*- coding: utf-8 -*-
> Changes with Apache 2.5.0
> 
> +  *) mod_authn_file, mod_authn_dbd, mod_authn_dbm, mod_authn_socache:
> +     Cache the result of the most recent password hash verification for every
> +     keep-alive connection. This saves some expensive calculations.
> +     [Stefan Fritsch]
> +
>   *) http: Remove support for Request-Range header sent by Navigator 2-3 and
>      MSIE 3. [Stefan Fritsch]
> 
> 
> Modified: httpd/httpd/trunk/include/ap_mmn.h
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/include/ap_mmn.h (original)
> +++ httpd/httpd/trunk/include/ap_mmn.h Tue Jan  1 20:16:30 2013
> @@ -412,6 +412,7 @@
>  *                         core_server_config again, add http09_enable
>  * 20121222.1 (2.5.0-dev)  Add http_conformance to core_server_config,
>  *                         add ap_has_cntrl()
> + * 20121222.2 (2.5.0-dev)  Add ap_password_validate()
>  */
> 
> #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
> @@ -419,7 +420,7 @@
> #ifndef MODULE_MAGIC_NUMBER_MAJOR
> #define MODULE_MAGIC_NUMBER_MAJOR 20121222
> #endif
> -#define MODULE_MAGIC_NUMBER_MINOR 1                   /* 0...n */
> +#define MODULE_MAGIC_NUMBER_MINOR 2                   /* 0...n */
> 
> /**
>  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
> 
> Modified: httpd/httpd/trunk/include/httpd.h
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/httpd.h?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/include/httpd.h (original)
> +++ httpd/httpd/trunk/include/httpd.h Tue Jan  1 20:16:30 2013
> @@ -2274,6 +2274,24 @@ AP_DECLARE(void) ap_bin2hex(const void *
> AP_DECLARE(int) ap_has_cntrl(const char *str)
>                 AP_FN_ATTR_NONNULL_ALL;
> 
> +/**
> + * Wrapper for @a apr_password_validate() to cache expensive calculations
> + * @param r the current request
> + * @param username username of the user
> + * @param passwd password string
> + * @param hash hash string to be passwd to @a apr_password_validate()
> + * @return APR_SUCCESS if passwords match, APR_EMISMATCH or error otherwise
> + * @note Currently, ap_password_validate() only caches the result of the
> + *       most recent call with the same connection as @a r.
> + *       In the future, it may also do rate-limiting against brute-force
> + *       attacks.
> + */
> +AP_DECLARE(apr_status_t) ap_password_validate(request_rec *r,
> +                                              const char *username,
> +                                              const char *passwd,
> +                                              const char *hash);
> +
> +
> #define AP_NORESTART APR_OS_START_USEERR + 1
> 
> #ifdef __cplusplus
> 
> Modified: httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c (original)
> +++ httpd/httpd/trunk/modules/aaa/mod_authn_dbd.c Tue Jan  1 20:16:30 2013
> @@ -179,7 +179,7 @@ static authn_status authn_dbd_password(r
>     }
>     AUTHN_CACHE_STORE(r, user, NULL, dbd_password);
> 
> -    rv = apr_password_validate(password, dbd_password);
> +    rv = ap_password_validate(r, user, password, dbd_password);
> 
>     if (rv != APR_SUCCESS) {
>         return AUTH_DENIED;
> 
> Modified: httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c (original)
> +++ httpd/httpd/trunk/modules/aaa/mod_authn_dbm.c Tue Jan  1 20:16:30 2013
> @@ -27,7 +27,6 @@
> #include "apr_want.h"
> #include "apr_strings.h"
> #include "apr_dbm.h"
> -#include "apr_md5.h"        /* for apr_password_validate */
> 
> #include "ap_provider.h"
> #include "httpd.h"
> @@ -144,7 +143,7 @@ static authn_status check_dbm_pw(request
>     }
>     AUTHN_CACHE_STORE(r, user, NULL, dbm_password);
> 
> -    rv = apr_password_validate(password, dbm_password);
> +    rv = ap_password_validate(r, user, password, dbm_password);
> 
>     if (rv != APR_SUCCESS) {
>         return AUTH_DENIED;
> 
> Modified: httpd/httpd/trunk/modules/aaa/mod_authn_file.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_file.c?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/aaa/mod_authn_file.c (original)
> +++ httpd/httpd/trunk/modules/aaa/mod_authn_file.c Tue Jan  1 20:16:30 2013
> @@ -15,7 +15,6 @@
>  */
> 
> #include "apr_strings.h"
> -#include "apr_md5.h"            /* for apr_password_validate */
> 
> #include "ap_config.h"
> #include "ap_provider.h"
> @@ -112,7 +111,7 @@ static authn_status check_password(reque
>     }
>     AUTHN_CACHE_STORE(r, user, NULL, file_password);
> 
> -    status = apr_password_validate(password, file_password);
> +    status = ap_password_validate(r, user, password, file_password);
>     if (status != APR_SUCCESS) {
>         return AUTH_DENIED;
>     }
> 
> Modified: httpd/httpd/trunk/modules/aaa/mod_authn_socache.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authn_socache.c?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/aaa/mod_authn_socache.c (original)
> +++ httpd/httpd/trunk/modules/aaa/mod_authn_socache.c Tue Jan  1 20:16:30 2013
> @@ -15,7 +15,6 @@
>  */
> 
> #include "apr_strings.h"
> -#include "apr_md5.h"            /* for apr_password_validate */
> 
> #include "ap_config.h"
> #include "ap_provider.h"
> @@ -375,7 +374,7 @@ static authn_status check_password(reque
>         return AUTH_USER_NOT_FOUND;
>     }
> 
> -    rv = apr_password_validate(password, (char*) val);
> +    rv = ap_password_validate(r, user, password, (char*) val);
>     if (rv != APR_SUCCESS) {
>         return AUTH_DENIED;
>     }
> 
> Modified: httpd/httpd/trunk/server/util.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/util.c?rev=1427548&r1=1427547&r2=1427548&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/server/util.c (original)
> +++ httpd/httpd/trunk/server/util.c Tue Jan  1 20:16:30 2013
> @@ -30,6 +30,7 @@
> #include "apr.h"
> #include "apr_strings.h"
> #include "apr_lib.h"
> +#include "apr_md5.h"            /* for apr_password_validate */
> 
> #define APR_WANT_STDIO
> #define APR_WANT_STRFUNC
> @@ -2896,3 +2897,42 @@ AP_DECLARE(void) ap_get_loadavg(ap_loada
>     }
> #endif
> }
> +
> +static const char * const pw_cache_note_name = "conn_cache_note";
> +struct pw_cache {
> +    /* varbuf contains concatenated password and hash */
> +    struct ap_varbuf vb;
> +    apr_size_t pwlen;
> +    apr_status_t result;
> +};
> +
> +AP_DECLARE(apr_status_t) ap_password_validate(request_rec *r,
> +                                              const char *username,
> +                                              const char *passwd,
> +                                              const char *hash)
> +{
> +    struct pw_cache *cache;
> +    apr_size_t hashlen;
> +
> +    cache = (struct pw_cache *)apr_table_get(r->connection->notes, pw_cache_note_name);
> +    if (cache != NULL) {
> +        if (strncmp(passwd, cache->vb.buf, cache->pwlen) == 0
> +            && strcmp(hash, cache->vb.buf + cache->pwlen) == 0) {
> +            return cache->result;
> +        }
> +        /* make ap_varbuf_grow below not copy the old data */
> +        cache->vb.strlen = 0;
> +    }
> +    else {
> +        cache = apr_palloc(r->connection->pool, sizeof(struct pw_cache));
> +        ap_varbuf_init(r->connection->pool, &cache->vb, 0);
> +        apr_table_setn(r->connection->notes, pw_cache_note_name, (void *)cache);
> +    }
> +    cache->pwlen = strlen(passwd);
> +    hashlen = strlen(hash);
> +    ap_varbuf_grow(&cache->vb, cache->pwlen + hashlen + 1);
> +    memcpy(cache->vb.buf, passwd, cache->pwlen);
> +    memcpy(cache->vb.buf + cache->pwlen, hash, hashlen + 1);
> +    cache->result = apr_password_validate(passwd, hash);
> +    return cache->result;
> +}
> 
>