dovecot-2.1: auth: Added CACHE-FLUSH command to flush some/all u...
dovecot at dovecot.org
dovecot at dovecot.org
Wed Jul 4 10:58:52 EEST 2012
details: http://hg.dovecot.org/dovecot-2.1/rev/007bf0047ab0
changeset: 14599:007bf0047ab0
user: Timo Sirainen <tss at iki.fi>
date: Wed Jul 04 10:56:53 2012 +0300
description:
auth: Added CACHE-FLUSH command to flush some/all users from auth cache.
diffstat:
src/auth/auth-cache.c | 228 +++++++++++++++++++++++++++----------
src/auth/auth-cache.h | 6 +-
src/auth/auth-master-connection.c | 27 ++++
src/auth/auth-request.h | 4 +
src/auth/auth.c | 9 +
src/auth/test-auth-cache.c | 18 ++-
6 files changed, 226 insertions(+), 66 deletions(-)
diffs (truncated from 491 to 300 lines):
diff -r c1a97e799b43 -r 007bf0047ab0 src/auth/auth-cache.c
--- a/src/auth/auth-cache.c Tue Jul 03 05:15:14 2012 +0300
+++ b/src/auth/auth-cache.c Wed Jul 04 10:56:53 2012 +0300
@@ -23,36 +23,72 @@
unsigned long long pos_size, neg_size;
};
-static const struct var_expand_table *
-auth_request_var_expand_tab_find(const char *key, unsigned int size)
+static bool
+auth_request_var_expand_tab_find(const char *key, unsigned int size,
+ unsigned int *idx_r)
{
const struct var_expand_table *tab = auth_request_var_expand_static_tab;
unsigned int i;
for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) {
if (size == 1) {
- if (key[0] == tab[i].key)
- return &tab[i];
+ if (key[0] == tab[i].key) {
+ *idx_r = i;
+ return TRUE;
+ }
} else if (tab[i].long_key != NULL) {
if (strncmp(key, tab[i].long_key, size) == 0 &&
- tab[i].long_key[size] == '\0')
- return &tab[i];
+ tab[i].long_key[size] == '\0') {
+ *idx_r = i;
+ return TRUE;
+ }
}
}
- return NULL;
+ return FALSE;
+}
+
+static void
+auth_cache_key_add_var(string_t *str, const char *data, unsigned int len)
+{
+ if (str_len(str) > 0)
+ str_append_c(str, '\t');
+ str_append_c(str, '%');
+ if (len == 1)
+ str_append_c(str, data[0]);
+ else {
+ str_append_c(str, '{');
+ str_append_n(str, data, len);
+ str_append_c(str, '}');
+ }
+}
+
+static void auth_cache_key_add_tab_idx(string_t *str, unsigned int i)
+{
+ const struct var_expand_table *tab =
+ &auth_request_var_expand_static_tab[i];
+
+ if (str_len(str) > 0)
+ str_append_c(str, '\t');
+ str_append_c(str, '%');
+ if (tab->key != '\0')
+ str_append_c(str, tab->key);
+ else {
+ str_append_c(str, '{');
+ str_append(str, tab->long_key);
+ str_append_c(str, '}');
+ }
}
char *auth_cache_parse_key(pool_t pool, const char *query)
{
- const struct var_expand_table *tab;
string_t *str;
- bool key_seen[100];
- unsigned int idx, size, tab_idx;
- bool add_key;
+ bool key_seen[AUTH_REQUEST_VAR_TAB_COUNT];
+ const char *extra_vars;
+ unsigned int i, idx, size, tab_idx;
memset(key_seen, 0, sizeof(key_seen));
- str = str_new(pool, 32);
+ str = t_str_new(32);
for (; *query != '\0'; ) {
if (*query != '%') {
query++;
@@ -66,34 +102,45 @@
}
query += idx;
- tab = auth_request_var_expand_tab_find(query, size);
- if (tab == NULL) {
+ if (!auth_request_var_expand_tab_find(query, size, &tab_idx)) {
/* just add the key. it would be nice to prevent
duplicates here as well, but that's just too
much trouble and probably very rare. */
- add_key = TRUE;
+ auth_cache_key_add_var(str, query, size);
} else {
- tab_idx = tab - auth_request_var_expand_static_tab;
i_assert(tab_idx < N_ELEMENTS(key_seen));
- /* @UNSAFE */
- add_key = !key_seen[tab_idx];
key_seen[tab_idx] = TRUE;
}
- if (add_key) {
- if (str_len(str) != 0)
- str_append_c(str, '\t');
- str_append_c(str, '%');
- if (size == 1)
- str_append_c(str, query[0]);
- else {
- str_append_c(str, '{');
- str_append_n(str, query, size);
- str_append_c(str, '}');
- }
- }
query += size;
}
- return str_free_without_data(&str);
+
+ if (key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] &&
+ key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX]) {
+ /* %n and %d both used -> replace with %u */
+ key_seen[AUTH_REQUEST_VAR_TAB_USER_IDX] = TRUE;
+ key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] = FALSE;
+ key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX] = FALSE;
+ }
+
+ /* we rely on these being at the beginning */
+ i_assert(AUTH_REQUEST_VAR_TAB_USER_IDX == 0);
+ i_assert(AUTH_REQUEST_VAR_TAB_USERNAME_IDX == 1);
+ i_assert(AUTH_REQUEST_VAR_TAB_DOMAIN_IDX == 2);
+
+ extra_vars = t_strdup(str_c(str));
+ str_truncate(str, 0);
+ for (i = 0; i < N_ELEMENTS(key_seen); i++) {
+ if (key_seen[i])
+ auth_cache_key_add_tab_idx(str, i);
+ }
+
+ if (*extra_vars != '\0') {
+ if (str_len(str) > 0)
+ str_append_c(str, '\t');
+ str_append(str, extra_vars);
+ }
+
+ return p_strdup(pool, str_c(str));
}
static void
@@ -142,8 +189,8 @@
{
struct auth_cache *cache = context;
- i_info("SIGHUP received, clearing cache");
- auth_cache_clear(cache);
+ i_info("SIGHUP received, %u cache entries flushed",
+ auth_cache_clear(cache));
}
static void sig_auth_cache_stats(const siginfo_t *si ATTR_UNUSED, void *context)
@@ -200,11 +247,69 @@
i_free(cache);
}
-void auth_cache_clear(struct auth_cache *cache)
+unsigned int auth_cache_clear(struct auth_cache *cache)
{
+ unsigned int ret = hash_table_count(cache->hash);
+
while (cache->tail != NULL)
auth_cache_node_destroy(cache, cache->tail);
hash_table_clear(cache->hash, FALSE);
+ return ret;
+}
+
+static bool auth_cache_node_is_user(struct auth_cache_node *node,
+ const char *username)
+{
+ const char *data = node->data;
+ unsigned int username_len;
+
+ /* The cache nodes begin with "P"/"U", passdb/userdb ID, "/" and
+ then usually followed by the username. It's too much trouble to
+ keep track of all the cache keys, so we'll just match it as if it
+ was the username. If e.g. '%n' is used in the cache key instead of
+ '%u', it means that cache entries can be removed only when @domain
+ isn't in the username parameter. */
+ if (*data != 'P' && *data != 'U')
+ return FALSE;
+ data++;
+
+ while (*data >= '0' && *data <= '9')
+ data++;
+ if (*data != '/')
+ return FALSE;
+ data++;
+
+ username_len = strlen(username);
+ return strncmp(data, username, username_len) == 0 &&
+ (data[username_len] == '\t' || data[username_len] == '\0');
+}
+
+static bool auth_cache_node_is_one_of_users(struct auth_cache_node *node,
+ const char *const *usernames)
+{
+ unsigned int i;
+
+ for (i = 0; usernames[i] != NULL; i++) {
+ if (auth_cache_node_is_user(node, usernames[i]))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+unsigned int auth_cache_clear_users(struct auth_cache *cache,
+ const char *const *usernames)
+{
+ struct auth_cache_node *node, *next;
+ unsigned int ret = 0;
+
+ for (node = cache->tail; node != NULL; node = next) {
+ next = node->next;
+ if (auth_cache_node_is_one_of_users(node, usernames)) {
+ auth_cache_node_destroy(cache, cache->tail);
+ ret++;
+ }
+ }
+ return ret;
}
static const char *
@@ -216,12 +321,27 @@
return str_tabescape(string);
}
+static const char *
+auth_request_expand_cache_key(const struct auth_request *request,
+ const char *key)
+{
+ string_t *str;
+
+ /* Uniquely identify the request's passdb/userdb with the P/U prefix
+ and by "%!", which expands to the passdb/userdb ID number. */
+ key = t_strconcat(request->userdb_lookup ? "U" : "P", "%!/", key, NULL);
+
+ str = t_str_new(256);
+ var_expand(str, key,
+ auth_request_get_var_expand_table(request, auth_cache_escape));
+ return str_c(str);
+}
+
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, bool *neg_expired_r)
{
- string_t *str;
struct auth_cache_node *node;
const char *value;
unsigned int ttl_secs;
@@ -230,13 +350,8 @@
*expired_r = FALSE;
*neg_expired_r = FALSE;
- /* %! is prepended automatically. it contains the passdb ID number. */
- str = t_str_new(256);
- var_expand(str, t_strconcat(request->userdb_lookup ? "U" : "P",
- "%!/", key, NULL),
- auth_request_get_var_expand_table(request, auth_cache_escape));
-
- node = hash_table_lookup(cache->hash, str_c(str));
+ key = auth_request_expand_cache_key(request, key);
+ node = hash_table_lookup(cache->hash, key);
if (node == NULL) {
cache->miss_count++;
return NULL;
@@ -269,9 +384,8 @@
void auth_cache_insert(struct auth_cache *cache, struct auth_request *request,
const char *key, const char *value, bool last_success)
{
- string_t *str;
struct auth_cache_node *node;
- size_t data_size, alloc_size, value_len = strlen(value);
+ size_t data_size, alloc_size, key_len, value_len = strlen(value);
char *current_username;
if (*value == '\0' && cache->neg_ttl_secs == 0) {
@@ -286,15 +400,12 @@
request->requested_login_user == NULL)
request->user = t_strdup_noconst(request->translated_username);
- /* %! is prepended automatically. it contains the db ID number. */
- str = t_str_new(256);
- var_expand(str, t_strconcat(request->userdb_lookup ? "U" : "P",
- "%!/", key, NULL),
- auth_request_get_var_expand_table(request, auth_cache_escape));
More information about the dovecot-cvs
mailing list