~ubuntu-branches/ubuntu/utopic/dovecot/utopic-proposed

« back to all changes in this revision

Viewing changes to src/auth/auth-cache.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (4.1.35 sid)
  • Revision ID: package-import@ubuntu.com-20140108093549-i72o93pux8p0dlaf
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2004-2012 Dovecot authors, see the included COPYING file */
 
1
/* Copyright (c) 2004-2013 Dovecot authors, see the included COPYING file */
2
2
 
3
3
#include "auth-common.h"
4
4
#include "lib-signals.h"
12
12
#include <time.h>
13
13
 
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;
17
17
 
18
 
        size_t size_left;
 
18
        size_t max_size, size_left;
19
19
        unsigned int ttl_secs, neg_ttl_secs;
20
20
 
21
21
        unsigned int hit_count, miss_count;
23
23
        unsigned long long pos_size, neg_size;
24
24
};
25
25
 
26
 
static const struct var_expand_table *
27
 
auth_request_var_expand_tab_find(const char *key, unsigned int size)
 
26
static bool
 
27
auth_request_var_expand_tab_find(const char *key, unsigned int size,
 
28
                                 unsigned int *idx_r)
28
29
{
29
30
        const struct var_expand_table *tab = auth_request_var_expand_static_tab;
30
31
        unsigned int i;
31
32
 
32
33
        for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) {
33
34
                if (size == 1) {
34
 
                        if (key[0] == tab[i].key)
35
 
                                return &tab[i];
 
35
                        if (key[0] == tab[i].key) {
 
36
                                *idx_r = i;
 
37
                                return TRUE;
 
38
                        }
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')
39
 
                                return &tab[i];
 
41
                            tab[i].long_key[size] == '\0') {
 
42
                                *idx_r = i;
 
43
                                return TRUE;
 
44
                        }
40
45
                }
41
46
        }
42
 
        return NULL;
 
47
        return FALSE;
 
48
}
 
49
 
 
50
static void
 
51
auth_cache_key_add_var(string_t *str, const char *data, unsigned int len)
 
52
{
 
53
        if (str_len(str) > 0)
 
54
                str_append_c(str, '\t');
 
55
        str_append_c(str, '%');
 
56
        if (len == 1)
 
57
                str_append_c(str, data[0]);
 
58
        else {
 
59
                str_append_c(str, '{');
 
60
                str_append_n(str, data, len);
 
61
                str_append_c(str, '}');
 
62
        }
 
63
}
 
64
 
 
65
static void auth_cache_key_add_tab_idx(string_t *str, unsigned int i)
 
66
{
 
67
        const struct var_expand_table *tab =
 
68
                &auth_request_var_expand_static_tab[i];
 
69
 
 
70
        if (str_len(str) > 0)
 
71
                str_append_c(str, '\t');
 
72
        str_append_c(str, '%');
 
73
        if (tab->key != '\0')
 
74
                str_append_c(str, tab->key);
 
75
        else {
 
76
                str_append_c(str, '{');
 
77
                str_append(str, tab->long_key);
 
78
                str_append_c(str, '}');
 
79
        }
43
80
}
44
81
 
45
82
char *auth_cache_parse_key(pool_t pool, const char *query)
46
83
{
47
 
        const struct var_expand_table *tab;
48
84
        string_t *str;
49
 
        bool key_seen[100];
50
 
        unsigned int idx, size, tab_idx;
51
 
        bool add_key;
 
85
        bool key_seen[AUTH_REQUEST_VAR_TAB_COUNT];
 
86
        const char *extra_vars;
 
87
        unsigned int i, idx, size, tab_idx;
52
88
 
53
89
        memset(key_seen, 0, sizeof(key_seen));
54
90
 
55
 
        str = str_new(pool, 32);
 
91
        str = t_str_new(32);
56
92
        for (; *query != '\0'; ) {
57
93
                if (*query != '%') {
58
94
                        query++;
66
102
                }
67
103
                query += idx;
68
104
 
69
 
                tab = auth_request_var_expand_tab_find(query, size);
70
 
                if (tab == NULL) {
 
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. */
74
 
                        add_key = TRUE;
 
109
                        auth_cache_key_add_var(str, query, size);
75
110
                } else {
76
 
                        tab_idx = tab - auth_request_var_expand_static_tab;
77
111
                        i_assert(tab_idx < N_ELEMENTS(key_seen));
78
 
                        /* @UNSAFE */
79
 
                        add_key = !key_seen[tab_idx];
80
112
                        key_seen[tab_idx] = TRUE;
81
113
                }
82
 
                if (add_key) {
83
 
                        if (str_len(str) != 0)
84
 
                                str_append_c(str, '\t');
85
 
                        str_append_c(str, '%');
86
 
                        if (size == 1)
87
 
                                str_append_c(str, query[0]);
88
 
                        else {
89
 
                                str_append_c(str, '{');
90
 
                                str_append_n(str, query, size);
91
 
                                str_append_c(str, '}');
92
 
                        }
93
 
                }
94
114
                query += size;
95
115
        }
96
 
        return str_free_without_data(&str);
 
116
 
 
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;
 
123
        }
 
124
 
 
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);
 
129
 
 
130
        extra_vars = t_strdup(str_c(str));
 
131
        str_truncate(str, 0);
 
132
        for (i = 0; i < N_ELEMENTS(key_seen); i++) {
 
133
                if (key_seen[i])
 
134
                        auth_cache_key_add_tab_idx(str, i);
 
135
        }
 
136
 
 
137
        if (*extra_vars != '\0') {
 
138
                if (str_len(str) > 0)
 
139
                        str_append_c(str, '\t');
 
140
                str_append(str, extra_vars);
 
141
        }
 
142
 
 
143
        return p_strdup(pool, str_c(str));
97
144
}
98
145
 
99
146
static void
131
178
static void
132
179
auth_cache_node_destroy(struct auth_cache *cache, struct auth_cache_node *node)
133
180
{
 
181
        char *key = node->data;
 
182
 
134
183
        auth_cache_node_unlink(cache, node);
135
184
 
136
185
        cache->size_left += node->alloc_size;
137
 
        hash_table_remove(cache->hash, node->data);
 
186
        hash_table_remove(cache->hash, key);
138
187
        i_free(node);
139
188
}
140
189
 
142
191
{
143
192
        struct auth_cache *cache = context;
144
193
 
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));
147
196
}
148
197
 
149
198
static void sig_auth_cache_stats(const siginfo_t *si ATTR_UNUSED, void *context)
150
199
{
151
200
        struct auth_cache *cache = context;
152
201
        unsigned int total_count;
 
202
        size_t cache_used;
153
203
 
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));
158
208
 
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);
163
214
 
 
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));
 
220
 
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;
175
232
 
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;
200
257
        i_free(cache);
201
258
}
202
259
 
203
 
void auth_cache_clear(struct auth_cache *cache)
 
260
unsigned int auth_cache_clear(struct auth_cache *cache)
204
261
{
 
262
        unsigned int ret = hash_table_count(cache->hash);
 
263
 
205
264
        while (cache->tail != NULL)
206
265
                auth_cache_node_destroy(cache, cache->tail);
207
266
        hash_table_clear(cache->hash, FALSE);
 
267
        return ret;
 
268
}
 
269
 
 
270
static bool auth_cache_node_is_user(struct auth_cache_node *node,
 
271
                                    const char *username)
 
272
{
 
273
        const char *data = node->data;
 
274
        unsigned int username_len;
 
275
 
 
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')
 
283
                return FALSE;
 
284
        data++;
 
285
 
 
286
        while (*data >= '0' && *data <= '9')
 
287
                data++;
 
288
        if (*data == '+') {
 
289
                /* skip over +master_user */
 
290
                while (*data != '\t' && *data != '\0')
 
291
                        data++;
 
292
        }
 
293
        if (*data != '\t')
 
294
                return FALSE;
 
295
        data++;
 
296
 
 
297
        username_len = strlen(username);
 
298
        return strncmp(data, username, username_len) == 0 &&
 
299
                (data[username_len] == '\t' || data[username_len] == '\0');
 
300
}
 
301
 
 
302
static bool auth_cache_node_is_one_of_users(struct auth_cache_node *node,
 
303
                                            const char *const *usernames)
 
304
{
 
305
        unsigned int i;
 
306
 
 
307
        for (i = 0; usernames[i] != NULL; i++) {
 
308
                if (auth_cache_node_is_user(node, usernames[i]))
 
309
                        return TRUE;
 
310
        }
 
311
        return FALSE;
 
312
}
 
313
 
 
314
unsigned int auth_cache_clear_users(struct auth_cache *cache,
 
315
                                    const char *const *usernames)
 
316
{
 
317
        struct auth_cache_node *node, *next;
 
318
        unsigned int ret = 0;
 
319
 
 
320
        for (node = cache->tail; node != NULL; node = next) {
 
321
                next = node->next;
 
322
                if (auth_cache_node_is_one_of_users(node, usernames)) {
 
323
                        auth_cache_node_destroy(cache, node);
 
324
                        ret++;
 
325
                }
 
326
        }
 
327
        return ret;
208
328
}
209
329
 
210
330
static const char *
216
336
        return str_tabescape(string);
217
337
}
218
338
 
 
339
static const char *
 
340
auth_request_expand_cache_key(const struct auth_request *request,
 
341
                              const char *key)
 
342
{
 
343
        string_t *str;
 
344
 
 
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}",
 
349
                          "\t", key, NULL);
 
350
 
 
351
        str = t_str_new(256);
 
352
        var_expand(str, key,
 
353
                   auth_request_get_var_expand_table(request, auth_cache_escape));
 
354
        return str_c(str);
 
355
}
 
356
 
219
357
const char *
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)
223
361
{
224
 
        string_t *str;
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;
232
369
 
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",
236
 
                                    "%!/", key, NULL),
237
 
                   auth_request_get_var_expand_table(request, auth_cache_escape));
238
 
 
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++;
242
374
                return NULL;
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)
271
403
{
272
 
        string_t *str;
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;
276
407
 
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);
288
420
 
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",
292
 
                                    "%!/", key, NULL),
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);
294
423
 
295
424
        request->user = current_username;
296
425
 
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;
300
429
 
302
431
        while (cache->size_left < alloc_size && cache->tail != NULL)
303
432
                auth_cache_node_destroy(cache, cache->tail);
304
433
 
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);
318
447
 
319
448
        auth_cache_node_link_head(cache, node);
320
449
 
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);
323
453
 
324
454
        if (*value != '\0') {
325
455
                cache->pos_entries++;
331
461
}
332
462
 
333
463
void auth_cache_remove(struct auth_cache *cache,
334
 
                       const struct auth_request *request,
335
 
                       const char *key)
 
464
                       const struct auth_request *request, const char *key)
336
465
{
337
 
        string_t *str;
338
466
        struct auth_cache_node *node;
339
467
 
340
 
        str = t_str_new(256);
341
 
        var_expand(str, key,
342
 
                   auth_request_get_var_expand_table(request, auth_cache_escape));
343
 
 
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)
346
471
                return;
347
472