bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "auth-common.h"
019a610a5ce14a80f6b017f94829933664879021Timo Sirainen#include "lib-signals.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "hash.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "str.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "strescape.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "var-expand.h"
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#include "auth-request.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "auth-cache.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include <time.h>
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstruct auth_cache {
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen HASH_TABLE(char *, struct auth_cache_node *) hash;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen struct auth_cache_node *head, *tail;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen size_t max_size, size_left;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen unsigned int ttl_secs, neg_ttl_secs;
078aac578d08e348836c53939782afb10479def5Timo Sirainen
078aac578d08e348836c53939782afb10479def5Timo Sirainen unsigned int hit_count, miss_count;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen unsigned int pos_entries, neg_entries;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen unsigned long long pos_size, neg_size;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen};
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenstatic bool
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenauth_request_var_expand_tab_find(const char *key, unsigned int size,
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen unsigned int *idx_r)
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen{
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen const struct var_expand_table *tab = auth_request_var_expand_static_tab;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen unsigned int i;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) {
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen if (size == 1) {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (key[0] == tab[i].key) {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen *idx_r = i;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return TRUE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen } else if (tab[i].long_key != NULL) {
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen if (strncmp(key, tab[i].long_key, size) == 0 &&
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen tab[i].long_key[size] == '\0') {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen *idx_r = i;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return TRUE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return FALSE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenstatic void
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenauth_cache_key_add_var(string_t *str, const char *data, unsigned int len)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (str_len(str) > 0)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '\t');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '%');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (len == 1)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, data[0]);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen else {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '{');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_n(str, data, len);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '}');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenstatic void auth_cache_key_add_tab_idx(string_t *str, unsigned int i)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const struct var_expand_table *tab =
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen &auth_request_var_expand_static_tab[i];
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (str_len(str) > 0)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '\t');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '%');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (tab->key != '\0')
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, tab->key);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen else {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '{');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append(str, tab->long_key);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '}');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen}
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen
12a34eb68607d0f8c7c3dc973ee605e89d44ba30Timo Sirainenchar *auth_cache_parse_key(pool_t pool, const char *query)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen string_t *str;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen bool key_seen[AUTH_REQUEST_VAR_TAB_COUNT];
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const char *extra_vars;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen unsigned int i, idx, size, tab_idx;
4e21cef121b8ad20439ccbcb4e9072c208f08611Timo Sirainen
4e21cef121b8ad20439ccbcb4e9072c208f08611Timo Sirainen memset(key_seen, 0, sizeof(key_seen));
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str = t_str_new(32);
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen for (; *query != '\0'; ) {
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen if (*query != '%') {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen query++;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen continue;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen var_get_key_range(++query, &idx, &size);
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen if (size == 0) {
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen /* broken %variable ending too early */
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen break;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen query += idx;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (!auth_request_var_expand_tab_find(query, size, &tab_idx)) {
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen /* just add the key. it would be nice to prevent
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen duplicates here as well, but that's just too
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen much trouble and probably very rare. */
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen auth_cache_key_add_var(str, query, size);
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen } else {
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen i_assert(tab_idx < N_ELEMENTS(key_seen));
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen key_seen[tab_idx] = TRUE;
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen }
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen query += size;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] &&
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX]) {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen /* %n and %d both used -> replace with %u */
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key_seen[AUTH_REQUEST_VAR_TAB_USER_IDX] = TRUE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] = FALSE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX] = FALSE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen /* we rely on these being at the beginning */
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen i_assert(AUTH_REQUEST_VAR_TAB_USER_IDX == 0);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen i_assert(AUTH_REQUEST_VAR_TAB_USERNAME_IDX == 1);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen i_assert(AUTH_REQUEST_VAR_TAB_DOMAIN_IDX == 2);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen extra_vars = t_strdup(str_c(str));
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_truncate(str, 0);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen for (i = 0; i < N_ELEMENTS(key_seen); i++) {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (key_seen[i])
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen auth_cache_key_add_tab_idx(str, i);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (*extra_vars != '\0') {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (str_len(str) > 0)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append_c(str, '\t');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen str_append(str, extra_vars);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return p_strdup(pool, str_c(str));
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstatic void
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainenauth_cache_node_unlink(struct auth_cache *cache, struct auth_cache_node *node)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (node->prev != NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen node->prev->next = node->next;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen else {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* unlinking tail */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->tail = node->next;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (node->next != NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen node->next->prev = node->prev;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen else {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* unlinking head */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->head = node->prev;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstatic void
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainenauth_cache_node_link_head(struct auth_cache *cache,
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen struct auth_cache_node *node)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen node->prev = cache->head;
5d2e422f9971ca9f0ad77508c1e7c315546e2765Timo Sirainen node->next = NULL;
5d2e422f9971ca9f0ad77508c1e7c315546e2765Timo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->head = node;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (node->prev != NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen node->prev->next = node;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen else
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->tail = node;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstatic void
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainenauth_cache_node_destroy(struct auth_cache *cache, struct auth_cache_node *node)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *key = node->data;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen auth_cache_node_unlink(cache, node);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->size_left += node->alloc_size;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_remove(cache->hash, key);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen i_free(node);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
1c7fa51b35231f375998f66d5756f214519218f8Timo Sirainenstatic void sig_auth_cache_clear(const siginfo_t *si ATTR_UNUSED, void *context)
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen{
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen struct auth_cache *cache = context;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen i_info("SIGHUP received, %u cache entries flushed",
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen auth_cache_clear(cache));
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen}
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
1c7fa51b35231f375998f66d5756f214519218f8Timo Sirainenstatic void sig_auth_cache_stats(const siginfo_t *si ATTR_UNUSED, void *context)
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen{
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen struct auth_cache *cache = context;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen unsigned int total_count;
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen size_t cache_used;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen total_count = cache->hit_count + cache->miss_count;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen i_info("Authentication cache hits %u/%u (%u%%)",
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen cache->hit_count, total_count,
cdc7e0c3492f8b68699e4d19f0c96ba67f880e48Timo Sirainen total_count == 0 ? 100 : (cache->hit_count * 100 / total_count));
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen i_info("Authentication cache inserts: "
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen "positive: %u entries %llu bytes, "
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen "negative: %u entries %llu bytes",
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->pos_entries, cache->pos_size,
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->neg_entries, cache->neg_size);
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen cache_used = cache->max_size - cache->size_left;
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen i_info("Authentication cache current size: "
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen "%"PRIuSIZE_T" bytes used of %"PRIuSIZE_T" bytes (%u%%)",
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen cache_used, cache->max_size,
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen (unsigned int)(cache_used * 100ULL / cache->max_size));
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen /* reset counters */
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen cache->hit_count = cache->miss_count = 0;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->pos_entries = cache->neg_entries = 0;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->pos_size = cache->neg_size = 0;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen}
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainenstruct auth_cache *auth_cache_new(size_t max_size, unsigned int ttl_secs,
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen unsigned int neg_ttl_secs
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct auth_cache *cache;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache = i_new(struct auth_cache, 1);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&cache->hash, default_pool, 0, str_hash, strcmp);
ac1c78cce5eaa1298e8a302ec592e3a46a9a254dTimo Sirainen cache->max_size = max_size;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->size_left = max_size;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->ttl_secs = ttl_secs;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen cache->neg_ttl_secs = neg_ttl_secs;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
5512d420d826a2f9d4e7cb4e4919e1864fe688b0Timo Sirainen lib_signals_set_handler(SIGHUP, LIBSIG_FLAGS_SAFE,
5512d420d826a2f9d4e7cb4e4919e1864fe688b0Timo Sirainen sig_auth_cache_clear, cache);
5512d420d826a2f9d4e7cb4e4919e1864fe688b0Timo Sirainen lib_signals_set_handler(SIGUSR2, LIBSIG_FLAGS_SAFE,
5512d420d826a2f9d4e7cb4e4919e1864fe688b0Timo Sirainen sig_auth_cache_stats, cache);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return cache;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid auth_cache_free(struct auth_cache **_cache)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct auth_cache *cache = *_cache;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen *_cache = NULL;
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen lib_signals_unset_handler(SIGHUP, sig_auth_cache_clear, cache);
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen lib_signals_unset_handler(SIGUSR2, sig_auth_cache_stats, cache);
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen
321221ddc2dedc4ad79839770765adc40d311a0dTimo Sirainen auth_cache_clear(cache);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_destroy(&cache->hash);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen i_free(cache);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenunsigned int auth_cache_clear(struct auth_cache *cache)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen unsigned int ret = hash_table_count(cache->hash);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen while (cache->tail != NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen auth_cache_node_destroy(cache, cache->tail);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_clear(cache->hash, FALSE);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return ret;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenstatic bool auth_cache_node_is_user(struct auth_cache_node *node,
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const char *username)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const char *data = node->data;
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t username_len;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen /* The cache nodes begin with "P"/"U", passdb/userdb ID, optional
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen "+" master user, "\t" and then usually followed by the username.
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen It's too much trouble to keep track of all the cache keys, so we'll
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen just match it as if it was the username. If e.g. '%n' is used in the
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen cache key instead of '%u', it means that cache entries can be
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen removed only when @domain isn't in the username parameter. */
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (*data != 'P' && *data != 'U')
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return FALSE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen data++;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen while (*data >= '0' && *data <= '9')
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen data++;
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen if (*data == '+') {
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen /* skip over +master_user */
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen while (*data != '\t' && *data != '\0')
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen data++;
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen }
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen if (*data != '\t')
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return FALSE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen data++;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen username_len = strlen(username);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return strncmp(data, username, username_len) == 0 &&
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen (data[username_len] == '\t' || data[username_len] == '\0');
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenstatic bool auth_cache_node_is_one_of_users(struct auth_cache_node *node,
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const char *const *usernames)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen unsigned int i;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen for (i = 0; usernames[i] != NULL; i++) {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (auth_cache_node_is_user(node, usernames[i]))
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return TRUE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return FALSE;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenunsigned int auth_cache_clear_users(struct auth_cache *cache,
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const char *const *usernames)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen struct auth_cache_node *node, *next;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen unsigned int ret = 0;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen for (node = cache->tail; node != NULL; node = next) {
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen next = node->next;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen if (auth_cache_node_is_one_of_users(node, usernames)) {
7a3d16456e88f49d02dbafb8a646457a9143fc32Timo Sirainen auth_cache_node_destroy(cache, node);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen ret++;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen }
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen return ret;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainenstatic const char *
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainenauth_cache_escape(const char *string,
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen const struct auth_request *auth_request ATTR_UNUSED)
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen{
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen /* cache key %variables are separated by tabs, make sure that there
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen are no tabs in the string */
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen return str_tabescape(string);
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen}
ca283b86783042541e15cfd17ef253a2ce48aa71Timo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenstatic const char *
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenauth_request_expand_cache_key(const struct auth_request *request,
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const char *key)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen static bool error_logged = FALSE;
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen const char *value, *error;
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen /* Uniquely identify the request's passdb/userdb with the P/U prefix
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen and by "%!", which expands to the passdb/userdb ID number. */
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen key = t_strconcat(request->userdb_lookup ? "U" : "P", "%!",
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen request->master_user == NULL ? "" : "+%{master_user}",
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen "\t", key, NULL);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen /* It's fine to have unknown %variables in the cache key.
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen For example db-ldap can have pass_attrs containing
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen %{ldap:fields} which are used for output, not as part of
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen the input needed for cache_key. Those could in theory be
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen filtered out early in the cache_key, but that gets more
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen problematic when it needs to support also filtering out
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen e.g. %{sha256:ldap:fields}. */
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen if (t_auth_request_var_expand(key, request, auth_cache_escape,
9c77606aef2dadad72e1a97f6d820dadb05d1d44Timo Sirainen &value, &error) < 0 && !error_logged) {
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen error_logged = TRUE;
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen i_error("Failed to expand auth cache key %s: %s", key, error);
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen }
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen return value;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainenconst char *
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainenauth_cache_lookup(struct auth_cache *cache, const struct auth_request *request,
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen const char *key, struct auth_cache_node **node_r,
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen bool *expired_r, bool *neg_expired_r)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen struct auth_cache_node *node;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen const char *value;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen unsigned int ttl_secs;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen time_t now;
019a610a5ce14a80f6b017f94829933664879021Timo Sirainen
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen *expired_r = FALSE;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen *neg_expired_r = FALSE;
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key = auth_request_expand_cache_key(request, key);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen node = hash_table_lookup(cache->hash, key);
078aac578d08e348836c53939782afb10479def5Timo Sirainen if (node == NULL) {
078aac578d08e348836c53939782afb10479def5Timo Sirainen cache->miss_count++;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return NULL;
078aac578d08e348836c53939782afb10479def5Timo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen value = node->data + strlen(node->data) + 1;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen ttl_secs = *value == '\0' ? cache->neg_ttl_secs : cache->ttl_secs;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen now = time(NULL);
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen if (node->created < now - (time_t)ttl_secs) {
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen /* TTL expired */
6a6a995b34fbfab76265b9db43a910efef962173Timo Sirainen cache->miss_count++;
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen *expired_r = TRUE;
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen } else {
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen /* move to head */
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen if (node != cache->head) {
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen auth_cache_node_unlink(cache, node);
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen auth_cache_node_link_head(cache, node);
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen }
6a6a995b34fbfab76265b9db43a910efef962173Timo Sirainen cache->hit_count++;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen if (node->created < now - (time_t)cache->neg_ttl_secs)
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen *neg_expired_r = TRUE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen if (node_r != NULL)
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen *node_r = node;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen return value;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
814b1e8e301bf58cc03ddf35a380b43c581af4dfTimo Sirainenvoid auth_cache_insert(struct auth_cache *cache, struct auth_request *request,
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen const char *key, const char *value, bool last_success)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen struct auth_cache_node *node;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen size_t data_size, alloc_size, key_len, value_len = strlen(value);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *hash_key, *current_username;
814b1e8e301bf58cc03ddf35a380b43c581af4dfTimo Sirainen
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen if (*value == '\0' && cache->neg_ttl_secs == 0) {
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen /* we're not caching negative entries */
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen return;
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen }
b522f391ef6aa4e527f2eeb5f7daa3da188790b0Timo Sirainen
d24a5c09373ec890da5736882c1756aa7fa651ebTimo Sirainen /* store into cache using the translated username, except if we're doing
814b1e8e301bf58cc03ddf35a380b43c581af4dfTimo Sirainen a master user login */
814b1e8e301bf58cc03ddf35a380b43c581af4dfTimo Sirainen current_username = request->user;
d24a5c09373ec890da5736882c1756aa7fa651ebTimo Sirainen if (request->translated_username != NULL &&
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen request->requested_login_user == NULL &&
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen request->master_user == NULL)
d24a5c09373ec890da5736882c1756aa7fa651ebTimo Sirainen request->user = t_strdup_noconst(request->translated_username);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key = auth_request_expand_cache_key(request, key);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key_len = strlen(key);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
814b1e8e301bf58cc03ddf35a380b43c581af4dfTimo Sirainen request->user = current_username;
814b1e8e301bf58cc03ddf35a380b43c581af4dfTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen data_size = key_len + 1 + value_len + 1;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen alloc_size = sizeof(struct auth_cache_node) -
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen sizeof(node->data) + data_size;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* make sure we have enough space */
1e50a4f930a028ab5fcf690503bfd13b54ff6787Timo Sirainen while (cache->size_left < alloc_size && cache->tail != NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen auth_cache_node_destroy(cache, cache->tail);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen node = hash_table_lookup(cache->hash, key);
e361b5386c77ee25685b5ad2bd2519a077dea882Timo Sirainen if (node != NULL) {
e361b5386c77ee25685b5ad2bd2519a077dea882Timo Sirainen /* key is already in cache (probably expired), remove it */
e361b5386c77ee25685b5ad2bd2519a077dea882Timo Sirainen auth_cache_node_destroy(cache, node);
e361b5386c77ee25685b5ad2bd2519a077dea882Timo Sirainen }
e361b5386c77ee25685b5ad2bd2519a077dea882Timo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* @UNSAFE */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen node = i_malloc(alloc_size);
e361b5386c77ee25685b5ad2bd2519a077dea882Timo Sirainen node->created = time(NULL);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen node->alloc_size = alloc_size;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen node->last_success = last_success;
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen memcpy(node->data, key, key_len);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen memcpy(node->data + key_len + 1, value, value_len);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen auth_cache_node_link_head(cache, node);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cache->size_left -= alloc_size;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_key = node->data;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_insert(cache->hash, hash_key, node);
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen if (*value != '\0') {
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->pos_entries++;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->pos_size += alloc_size;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen } else {
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->neg_entries++;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen cache->neg_size += alloc_size;
20df5a94b36be8e6c2d0e481973cab6715c61bb8Timo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainenvoid auth_cache_remove(struct auth_cache *cache,
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen const struct auth_request *request, const char *key)
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen{
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen struct auth_cache_node *node;
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen key = auth_request_expand_cache_key(request, key);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen node = hash_table_lookup(cache->hash, key);
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen if (node == NULL)
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen return;
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen auth_cache_node_destroy(cache, node);
121221dd2970a6f5ec601abb3b1c505c238e0b60Timo Sirainen}