14
14
struct auth_cache {
15
struct hash_table *hash;
15
HASH_TABLE(char *, struct auth_cache_node *) hash;
16
16
struct auth_cache_node *head, *tail;
18
size_t max_size, size_left;
19
19
unsigned int ttl_secs, neg_ttl_secs;
21
21
unsigned int hit_count, miss_count;
23
23
unsigned long long pos_size, neg_size;
26
static const struct var_expand_table *
27
auth_request_var_expand_tab_find(const char *key, unsigned int size)
27
auth_request_var_expand_tab_find(const char *key, unsigned int size,
29
30
const struct var_expand_table *tab = auth_request_var_expand_static_tab;
32
33
for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) {
34
if (key[0] == tab[i].key)
35
if (key[0] == tab[i].key) {
36
39
} else if (tab[i].long_key != NULL) {
37
40
if (strncmp(key, tab[i].long_key, size) == 0 &&
38
tab[i].long_key[size] == '\0')
41
tab[i].long_key[size] == '\0') {
51
auth_cache_key_add_var(string_t *str, const char *data, unsigned int len)
54
str_append_c(str, '\t');
55
str_append_c(str, '%');
57
str_append_c(str, data[0]);
59
str_append_c(str, '{');
60
str_append_n(str, data, len);
61
str_append_c(str, '}');
65
static void auth_cache_key_add_tab_idx(string_t *str, unsigned int i)
67
const struct var_expand_table *tab =
68
&auth_request_var_expand_static_tab[i];
71
str_append_c(str, '\t');
72
str_append_c(str, '%');
74
str_append_c(str, tab->key);
76
str_append_c(str, '{');
77
str_append(str, tab->long_key);
78
str_append_c(str, '}');
45
82
char *auth_cache_parse_key(pool_t pool, const char *query)
47
const struct var_expand_table *tab;
50
unsigned int idx, size, tab_idx;
85
bool key_seen[AUTH_REQUEST_VAR_TAB_COUNT];
86
const char *extra_vars;
87
unsigned int i, idx, size, tab_idx;
53
89
memset(key_seen, 0, sizeof(key_seen));
55
str = str_new(pool, 32);
56
92
for (; *query != '\0'; ) {
57
93
if (*query != '%') {
69
tab = auth_request_var_expand_tab_find(query, size);
105
if (!auth_request_var_expand_tab_find(query, size, &tab_idx)) {
71
106
/* just add the key. it would be nice to prevent
72
107
duplicates here as well, but that's just too
73
108
much trouble and probably very rare. */
109
auth_cache_key_add_var(str, query, size);
76
tab_idx = tab - auth_request_var_expand_static_tab;
77
111
i_assert(tab_idx < N_ELEMENTS(key_seen));
79
add_key = !key_seen[tab_idx];
80
112
key_seen[tab_idx] = TRUE;
83
if (str_len(str) != 0)
84
str_append_c(str, '\t');
85
str_append_c(str, '%');
87
str_append_c(str, query[0]);
89
str_append_c(str, '{');
90
str_append_n(str, query, size);
91
str_append_c(str, '}');
96
return str_free_without_data(&str);
117
if (key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] &&
118
key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX]) {
119
/* %n and %d both used -> replace with %u */
120
key_seen[AUTH_REQUEST_VAR_TAB_USER_IDX] = TRUE;
121
key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] = FALSE;
122
key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX] = FALSE;
125
/* we rely on these being at the beginning */
126
i_assert(AUTH_REQUEST_VAR_TAB_USER_IDX == 0);
127
i_assert(AUTH_REQUEST_VAR_TAB_USERNAME_IDX == 1);
128
i_assert(AUTH_REQUEST_VAR_TAB_DOMAIN_IDX == 2);
130
extra_vars = t_strdup(str_c(str));
131
str_truncate(str, 0);
132
for (i = 0; i < N_ELEMENTS(key_seen); i++) {
134
auth_cache_key_add_tab_idx(str, i);
137
if (*extra_vars != '\0') {
138
if (str_len(str) > 0)
139
str_append_c(str, '\t');
140
str_append(str, extra_vars);
143
return p_strdup(pool, str_c(str));
132
179
auth_cache_node_destroy(struct auth_cache *cache, struct auth_cache_node *node)
181
char *key = node->data;
134
183
auth_cache_node_unlink(cache, node);
136
185
cache->size_left += node->alloc_size;
137
hash_table_remove(cache->hash, node->data);
186
hash_table_remove(cache->hash, key);
143
192
struct auth_cache *cache = context;
145
i_info("SIGHUP received, clearing cache");
146
auth_cache_clear(cache);
194
i_info("SIGHUP received, %u cache entries flushed",
195
auth_cache_clear(cache));
149
198
static void sig_auth_cache_stats(const siginfo_t *si ATTR_UNUSED, void *context)
151
200
struct auth_cache *cache = context;
152
201
unsigned int total_count;
154
204
total_count = cache->hit_count + cache->miss_count;
155
205
i_info("Authentication cache hits %u/%u (%u%%)",
157
207
total_count == 0 ? 100 : (cache->hit_count * 100 / total_count));
159
209
i_info("Authentication cache inserts: "
160
"positive: %u %lluB, negative: %u %lluB",
210
"positive: %u entries %llu bytes, "
211
"negative: %u entries %llu bytes",
161
212
cache->pos_entries, cache->pos_size,
162
213
cache->neg_entries, cache->neg_size);
215
cache_used = cache->max_size - cache->size_left;
216
i_info("Authentication cache current size: "
217
"%"PRIuSIZE_T" bytes used of %"PRIuSIZE_T" bytes (%u%%)",
218
cache_used, cache->max_size,
219
(unsigned int)(cache_used * 100ULL / cache->max_size));
164
221
/* reset counters */
165
222
cache->hit_count = cache->miss_count = 0;
166
223
cache->pos_entries = cache->neg_entries = 0;
174
231
struct auth_cache *cache;
176
233
cache = i_new(struct auth_cache, 1);
177
cache->hash = hash_table_create(default_pool, default_pool, 0, str_hash,
178
(hash_cmp_callback_t *)strcmp);
234
hash_table_create(&cache->hash, default_pool, 0, str_hash, strcmp);
235
cache->max_size = max_size;
179
236
cache->size_left = max_size;
180
237
cache->ttl_secs = ttl_secs;
181
238
cache->neg_ttl_secs = neg_ttl_secs;
203
void auth_cache_clear(struct auth_cache *cache)
260
unsigned int auth_cache_clear(struct auth_cache *cache)
262
unsigned int ret = hash_table_count(cache->hash);
205
264
while (cache->tail != NULL)
206
265
auth_cache_node_destroy(cache, cache->tail);
207
266
hash_table_clear(cache->hash, FALSE);
270
static bool auth_cache_node_is_user(struct auth_cache_node *node,
271
const char *username)
273
const char *data = node->data;
274
unsigned int username_len;
276
/* The cache nodes begin with "P"/"U", passdb/userdb ID, optional
277
"+" master user, "\t" and then usually followed by the username.
278
It's too much trouble to keep track of all the cache keys, so we'll
279
just match it as if it was the username. If e.g. '%n' is used in the
280
cache key instead of '%u', it means that cache entries can be
281
removed only when @domain isn't in the username parameter. */
282
if (*data != 'P' && *data != 'U')
286
while (*data >= '0' && *data <= '9')
289
/* skip over +master_user */
290
while (*data != '\t' && *data != '\0')
297
username_len = strlen(username);
298
return strncmp(data, username, username_len) == 0 &&
299
(data[username_len] == '\t' || data[username_len] == '\0');
302
static bool auth_cache_node_is_one_of_users(struct auth_cache_node *node,
303
const char *const *usernames)
307
for (i = 0; usernames[i] != NULL; i++) {
308
if (auth_cache_node_is_user(node, usernames[i]))
314
unsigned int auth_cache_clear_users(struct auth_cache *cache,
315
const char *const *usernames)
317
struct auth_cache_node *node, *next;
318
unsigned int ret = 0;
320
for (node = cache->tail; node != NULL; node = next) {
322
if (auth_cache_node_is_one_of_users(node, usernames)) {
323
auth_cache_node_destroy(cache, node);
210
330
static const char *
216
336
return str_tabescape(string);
340
auth_request_expand_cache_key(const struct auth_request *request,
345
/* Uniquely identify the request's passdb/userdb with the P/U prefix
346
and by "%!", which expands to the passdb/userdb ID number. */
347
key = t_strconcat(request->userdb_lookup ? "U" : "P", "%!",
348
request->master_user == NULL ? "" : "+%{master_user}",
351
str = t_str_new(256);
353
auth_request_get_var_expand_table(request, auth_cache_escape));
220
358
auth_cache_lookup(struct auth_cache *cache, const struct auth_request *request,
221
359
const char *key, struct auth_cache_node **node_r,
222
360
bool *expired_r, bool *neg_expired_r)
225
362
struct auth_cache_node *node;
226
363
const char *value;
227
364
unsigned int ttl_secs;
230
367
*expired_r = FALSE;
231
368
*neg_expired_r = FALSE;
233
/* %! is prepended automatically. it contains the passdb ID number. */
234
str = t_str_new(256);
235
var_expand(str, t_strconcat(request->userdb_lookup ? "U" : "P",
237
auth_request_get_var_expand_table(request, auth_cache_escape));
239
node = hash_table_lookup(cache->hash, str_c(str));
370
key = auth_request_expand_cache_key(request, key);
371
node = hash_table_lookup(cache->hash, key);
240
372
if (node == NULL) {
241
373
cache->miss_count++;
269
401
void auth_cache_insert(struct auth_cache *cache, struct auth_request *request,
270
402
const char *key, const char *value, bool last_success)
273
404
struct auth_cache_node *node;
274
size_t data_size, alloc_size, value_len = strlen(value);
275
char *current_username;
405
size_t data_size, alloc_size, key_len, value_len = strlen(value);
406
char *hash_key, *current_username;
277
408
if (*value == '\0' && cache->neg_ttl_secs == 0) {
278
409
/* we're not caching negative entries */
283
414
a master user login */
284
415
current_username = request->user;
285
416
if (request->translated_username != NULL &&
286
request->requested_login_user == NULL)
417
request->requested_login_user == NULL &&
418
request->master_user == NULL)
287
419
request->user = t_strdup_noconst(request->translated_username);
289
/* %! is prepended automatically. it contains the db ID number. */
290
str = t_str_new(256);
291
var_expand(str, t_strconcat(request->userdb_lookup ? "U" : "P",
293
auth_request_get_var_expand_table(request, auth_cache_escape));
421
key = auth_request_expand_cache_key(request, key);
422
key_len = strlen(key);
295
424
request->user = current_username;
297
data_size = str_len(str) + 1 + value_len + 1;
426
data_size = key_len + 1 + value_len + 1;
298
427
alloc_size = sizeof(struct auth_cache_node) -
299
428
sizeof(node->data) + data_size;
302
431
while (cache->size_left < alloc_size && cache->tail != NULL)
303
432
auth_cache_node_destroy(cache, cache->tail);
305
node = hash_table_lookup(cache->hash, str_c(str));
434
node = hash_table_lookup(cache->hash, key);
306
435
if (node != NULL) {
307
436
/* key is already in cache (probably expired), remove it */
308
437
auth_cache_node_destroy(cache, node);
313
442
node->created = time(NULL);
314
443
node->alloc_size = alloc_size;
315
444
node->last_success = last_success;
316
memcpy(node->data, str_data(str), str_len(str));
317
memcpy(node->data + str_len(str) + 1, value, value_len);
445
memcpy(node->data, key, key_len);
446
memcpy(node->data + key_len + 1, value, value_len);
319
448
auth_cache_node_link_head(cache, node);
321
450
cache->size_left -= alloc_size;
322
hash_table_insert(cache->hash, node->data, node);
451
hash_key = node->data;
452
hash_table_insert(cache->hash, hash_key, node);
324
454
if (*value != '\0') {
325
455
cache->pos_entries++;
333
463
void auth_cache_remove(struct auth_cache *cache,
334
const struct auth_request *request,
464
const struct auth_request *request, const char *key)
338
466
struct auth_cache_node *node;
340
str = t_str_new(256);
342
auth_request_get_var_expand_table(request, auth_cache_escape));
344
node = hash_table_lookup(cache->hash, str_c(str));
468
key = auth_request_expand_cache_key(request, key);
469
node = hash_table_lookup(cache->hash, key);
345
470
if (node == NULL)