From: Stefan Fritsch Date: Tue, 1 Jan 2013 20:16:30 +0000 (+0000) Subject: Add some caching for password hash validation. X-Git-Tag: 2.5.0-alpha~5928 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40431c8d757f8bdaf0c1be4203ea5f79224e31de;p=thirdparty%2Fapache%2Fhttpd.git 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 --- diff --git a/CHANGES b/CHANGES index 08de8de4189..6ce0791a074 100644 --- 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] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index b5f95364821..66aff78734e 100644 --- a/include/ap_mmn.h +++ b/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 diff --git a/include/httpd.h b/include/httpd.h index 0fd02a95b54..8f5d4f109b4 100644 --- a/include/httpd.h +++ b/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 diff --git a/modules/aaa/mod_authn_dbd.c b/modules/aaa/mod_authn_dbd.c index db5b05f682f..c4183c54a04 100644 --- a/modules/aaa/mod_authn_dbd.c +++ b/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; diff --git a/modules/aaa/mod_authn_dbm.c b/modules/aaa/mod_authn_dbm.c index 9ab05e45161..d969c7c63a4 100644 --- a/modules/aaa/mod_authn_dbm.c +++ b/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; diff --git a/modules/aaa/mod_authn_file.c b/modules/aaa/mod_authn_file.c index a54a423b244..9de7a4cc88b 100644 --- a/modules/aaa/mod_authn_file.c +++ b/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; } diff --git a/modules/aaa/mod_authn_socache.c b/modules/aaa/mod_authn_socache.c index cccd076b022..601997784db 100644 --- a/modules/aaa/mod_authn_socache.c +++ b/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; } diff --git a/server/util.c b/server/util.c index 7a8ba3b10b4..6f028904815 100644 --- a/server/util.c +++ b/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; +}