Skip to content
Browse files

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.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1427548 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 60e83d5 commit 737192d0332db6ae363208b4d34f007e5ee33901 Stefan Fritsch committed
View
5 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]
View
3 include/ap_mmn.h
@@ -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
View
18 include/httpd.h
@@ -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
View
2 modules/aaa/mod_authn_dbd.c
@@ -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;
View
3 modules/aaa/mod_authn_dbm.c
@@ -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;
View
3 modules/aaa/mod_authn_file.c
@@ -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;
}
View
3 modules/aaa/mod_authn_socache.c
@@ -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;
}
View
40 server/util.c
@@ -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;
+}

0 comments on commit 737192d

Please sign in to comment.
Something went wrong with that request. Please try again.