]> granicus.if.org Git - apache/commitdiff
Add some caching for password hash validation.
authorStefan Fritsch <sf@apache.org>
Tue, 1 Jan 2013 20:16:30 +0000 (20:16 +0000)
committerStefan Fritsch <sf@apache.org>
Tue, 1 Jan 2013 20:16:30 +0000 (20:16 +0000)
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.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1427548 13f79535-47bb-0310-9956-ffa450edef68

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

diff --git a/CHANGES b/CHANGES
index 08de8de41895534daeafb313e2cc2c80257ab9e3..6ce0791a074463aa67138cdbe10e5f21c2634972 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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]
 
index b5f95364821215fae60f968d1d9cd3a41cb60467..66aff78734e6d5aa60bde7ebb9cb317a6e9f5175 100644 (file)
  *                         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" */
 #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
index 0fd02a95b54dec1421c66a528a8a308da8f888e7..8f5d4f109b4bce103177b1f24ac5d8cd38333112 100644 (file)
@@ -2274,6 +2274,24 @@ AP_DECLARE(void) ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
 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
index db5b05f682f3c35d03974cec4db22268d8557ce2..c4183c54a04a5d4db23776820c1479f1862d365c 100644 (file)
@@ -179,7 +179,7 @@ static authn_status authn_dbd_password(request_rec *r, const char *user,
     }
     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;
index 9ab05e45161936f1d694f63b0c713e11c6d6c50c..d969c7c63a4fa319265e4afa3f3ed9d1bebfde35 100644 (file)
@@ -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_rec *r, const char *user,
     }
     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;
index a54a423b2449bb0136bff60601731c9c8847a8f8..9de7a4cc88b3f453d5aa27bb26c1a737c781903a 100644 (file)
@@ -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(request_rec *r, const char *user,
     }
     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;
     }
index cccd076b0220e44560f978ee8ca1d1baba92b0e7..601997784db87c8b2873715216d3249ff595e64e 100644 (file)
@@ -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(request_rec *r, const char *user,
         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;
     }
index 7a8ba3b10b446ac635b6a89d800d531c97644276..6f028904815fed118f7d4614f88f03ab45b3aa67 100644 (file)
@@ -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_loadavg_t *ld)
     }
 #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;
+}