# HG changeset patch # User Timo Sirainen # Date 1160757432 -10800 # Node ID 3b49b9ec87dc008870f847551580aaeb001d5424 # Parent a2a9b6a92831e57ec84ad247df4d19a56a677c9c auth_cache: Try to handle changing passwords automatically: If password verification fails, but the last one had succeeded, don't use the cache. This works only with plaintext auth. diff -r a2a9b6a92831 -r 3b49b9ec87dc dovecot-example.conf --- a/dovecot-example.conf Fri Oct 13 18:33:03 2006 +0300 +++ b/dovecot-example.conf Fri Oct 13 19:37:12 2006 +0300 @@ -647,7 +647,9 @@ #auth_cache_size = 0 # Time to live in seconds for cached data. After this many seconds the cached # record is no longer used, *except* if the main database lookup returns -# internal failure. +# internal failure. We also try to handle password changes automatically: If +# user's previous authentication was successful, but this one wasn't, the +# cache isn't used. For now this works only with plaintext authentication. #auth_cache_ttl = 3600 # Space separated list of realms for SASL authentication mechanisms that need diff -r a2a9b6a92831 -r 3b49b9ec87dc src/auth/auth-cache.c --- a/src/auth/auth-cache.c Fri Oct 13 18:33:03 2006 +0300 +++ b/src/auth/auth-cache.c Fri Oct 13 19:37:12 2006 +0300 @@ -11,16 +11,9 @@ #include -struct cache_node { - struct cache_node *prev, *next; - time_t created; - uint32_t alloc_size; - char data[4]; /* key \0 value \0 */ -}; - struct auth_cache { struct hash_table *hash; - struct cache_node *head, *tail; + struct auth_cache_node *head, *tail; size_t size_left; unsigned int ttl_secs; @@ -56,7 +49,7 @@ } static void -auth_cache_node_unlink(struct auth_cache *cache, struct cache_node *node) +auth_cache_node_unlink(struct auth_cache *cache, struct auth_cache_node *node) { if (node->prev != NULL) node->prev->next = node->next; @@ -74,7 +67,8 @@ } static void -auth_cache_node_link_head(struct auth_cache *cache, struct cache_node *node) +auth_cache_node_link_head(struct auth_cache *cache, + struct auth_cache_node *node) { node->prev = cache->head; node->next = NULL; @@ -87,7 +81,7 @@ } static void -auth_cache_node_destroy(struct auth_cache *cache, struct cache_node *node) +auth_cache_node_destroy(struct auth_cache *cache, struct auth_cache_node *node) { auth_cache_node_unlink(cache, node); @@ -153,12 +147,13 @@ hash_clear(cache->hash, FALSE); } -const char *auth_cache_lookup(struct auth_cache *cache, - const struct auth_request *request, - const char *key, bool *expired_r) +const char * +auth_cache_lookup(struct auth_cache *cache, const struct auth_request *request, + const char *key, struct auth_cache_node **node_r, + bool *expired_r) { string_t *str; - struct cache_node *node; + struct auth_cache_node *node; *expired_r = FALSE; @@ -185,15 +180,18 @@ } } + if (node_r != NULL) + *node_r = node; + return node->data + strlen(node->data) + 1; } void auth_cache_insert(struct auth_cache *cache, const struct auth_request *request, - const char *key, const char *value) + const char *key, const char *value, bool last_success) { string_t *str; - struct cache_node *node; + struct auth_cache_node *node; size_t data_size, alloc_size, value_len = strlen(value); str = t_str_new(256); @@ -202,7 +200,8 @@ auth_request_str_escape)); data_size = str_len(str) + 1 + value_len + 1; - alloc_size = sizeof(struct cache_node) - sizeof(node->data) + data_size; + alloc_size = sizeof(struct auth_cache_node) - + sizeof(node->data) + data_size; /* make sure we have enough space */ while (cache->size_left < alloc_size) @@ -218,6 +217,7 @@ node = i_malloc(alloc_size); node->created = time(NULL); node->alloc_size = alloc_size; + node->last_success = last_success; memcpy(node->data, str_data(str), str_len(str)); memcpy(node->data + str_len(str) + 1, value, value_len); diff -r a2a9b6a92831 -r 3b49b9ec87dc src/auth/auth-cache.h --- a/src/auth/auth-cache.h Fri Oct 13 18:33:03 2006 +0300 +++ b/src/auth/auth-cache.h Fri Oct 13 19:37:12 2006 +0300 @@ -1,6 +1,18 @@ #ifndef __AUTH_CACHE_H #define __AUTH_CACHE_H +struct auth_cache_node { + struct auth_cache_node *prev, *next; + + time_t created; + /* Total number of bytes used by this node */ + uint32_t alloc_size:31; + /* TRUE if the user gave the correct password the last time. */ + uint32_t last_success:1; + + char data[4]; /* key \0 value \0 */ +}; + struct auth_cache; struct auth_request; @@ -18,13 +30,15 @@ void auth_cache_clear(struct auth_cache *cache); /* Look key from cache. key should be the same string as returned by - auth_cache_parse_key(). */ -const char *auth_cache_lookup(struct auth_cache *cache, - const struct auth_request *request, - const char *key, bool *expired_r); + auth_cache_parse_key(). Returned node can't be used after any other + auth_cache_*() calls. */ +const char * +auth_cache_lookup(struct auth_cache *cache, const struct auth_request *request, + const char *key, struct auth_cache_node **node_r, + bool *expired_r); /* Insert key => value into cache. */ void auth_cache_insert(struct auth_cache *cache, const struct auth_request *request, - const char *key, const char *value); + const char *key, const char *value, bool last_success); #endif diff -r a2a9b6a92831 -r 3b49b9ec87dc src/auth/auth-request.c --- a/src/auth/auth-request.c Fri Oct 13 18:33:03 2006 +0300 +++ b/src/auth/auth-request.c Fri Oct 13 19:37:12 2006 +0300 @@ -216,7 +216,7 @@ /* lookup failed. */ if (result == PASSDB_RESULT_USER_UNKNOWN) { auth_cache_insert(passdb_cache, request, - passdb->cache_key, ""); + passdb->cache_key, "", FALSE); } return; } @@ -258,7 +258,8 @@ str_append_c(str, '\t'); str_append(str, "nodelay"); } - auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str)); + auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str), + result == PASSDB_RESULT_OK); } static bool auth_request_master_lookup_finish(struct auth_request *request) diff -r a2a9b6a92831 -r 3b49b9ec87dc src/auth/passdb-cache.c --- a/src/auth/passdb-cache.c Fri Oct 13 18:33:03 2006 +0300 +++ b/src/auth/passdb-cache.c Fri Oct 13 19:37:12 2006 +0300 @@ -37,6 +37,7 @@ enum passdb_result *result_r, int use_expired) { const char *value, *cached_pw, *scheme, *const *list; + struct auth_cache_node *node; int ret; bool expired; @@ -44,7 +45,7 @@ return FALSE; /* value = password \t ... */ - value = auth_cache_lookup(passdb_cache, request, key, &expired); + value = auth_cache_lookup(passdb_cache, request, key, &node, &expired); if (value == NULL || (expired && !use_expired)) return FALSE; @@ -72,6 +73,14 @@ ret = auth_request_password_verify(request, password, cached_pw, scheme, "cache"); + if (ret == 0 && node->last_success) { + /* the last authentication was successful. assume that the + password was changed and cache is expired. */ + node->last_success = FALSE; + return FALSE; + } + node->last_success = ret > 0; + *result_r = ret > 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_PASSWORD_MISMATCH; return TRUE; @@ -84,12 +93,13 @@ bool use_expired) { const char *value, *const *list; + struct auth_cache_node *node; bool expired; if (passdb_cache == NULL) return FALSE; - value = auth_cache_lookup(passdb_cache, request, key, &expired); + value = auth_cache_lookup(passdb_cache, request, key, &node, &expired); if (value == NULL || (expired && !use_expired)) return FALSE;