changeset 4658:3b49b9ec87dc HEAD

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.
author Timo Sirainen <tss@iki.fi>
date Fri, 13 Oct 2006 19:37:12 +0300
parents a2a9b6a92831
children 66d3f064ba40
files dovecot-example.conf src/auth/auth-cache.c src/auth/auth-cache.h src/auth/auth-request.c src/auth/passdb-cache.c
diffstat 5 files changed, 55 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 <time.h>
 
-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);
 
--- 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
--- 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)
--- 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;