auth-cache.c revision ac1c78cce5eaa1298e8a302ec592e3a46a9a254d
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/* Copyright (c) 2004-2013 Dovecot authors, see the included COPYING file */
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch HASH_TABLE(char *, struct auth_cache_node *) hash;
56d1345c43bbd28c36b7faa85e4163bd9e874290Timo Sirainenauth_request_var_expand_tab_find(const char *key, unsigned int size,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int *idx_r)
35e962a9186b4e9b2001628c1d7b55c24b33ce84Timo Sirainen const struct var_expand_table *tab = auth_request_var_expand_static_tab;
35e962a9186b4e9b2001628c1d7b55c24b33ce84Timo Sirainen unsigned int i;
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen for (i = 0; tab[i].key != '\0' || tab[i].long_key != NULL; i++) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (strncmp(key, tab[i].long_key, size) == 0 &&
7384b4e78eaab44693c985192276e31322155e32Stephan Boschauth_cache_key_add_var(string_t *str, const char *data, unsigned int len)
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void auth_cache_key_add_tab_idx(string_t *str, unsigned int i)
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Boschchar *auth_cache_parse_key(pool_t pool, const char *query)
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen /* broken %variable ending too early */
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen if (!auth_request_var_expand_tab_find(query, size, &tab_idx)) {
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen /* just add the key. it would be nice to prevent
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen duplicates here as well, but that's just too
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen much trouble and probably very rare. */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] &&
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch /* %n and %d both used -> replace with %u */
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch key_seen[AUTH_REQUEST_VAR_TAB_USER_IDX] = TRUE;
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] = FALSE;
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX] = FALSE;
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch /* we rely on these being at the beginning */
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch i_assert(AUTH_REQUEST_VAR_TAB_USERNAME_IDX == 1);
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch i_assert(AUTH_REQUEST_VAR_TAB_DOMAIN_IDX == 2);
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschauth_cache_node_unlink(struct auth_cache *cache, struct auth_cache_node *node)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch /* unlinking tail */
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch /* unlinking head */
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschauth_cache_node_link_head(struct auth_cache *cache,
7384b4e78eaab44693c985192276e31322155e32Stephan Boschauth_cache_node_destroy(struct auth_cache *cache, struct auth_cache_node *node)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschstatic void sig_auth_cache_clear(const siginfo_t *si ATTR_UNUSED, void *context)
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch i_info("SIGHUP received, %u cache entries flushed",
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschstatic void sig_auth_cache_stats(const siginfo_t *si ATTR_UNUSED, void *context)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch total_count = cache->hit_count + cache->miss_count;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch i_info("Authentication cache hits %u/%u (%u%%)",
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch total_count == 0 ? 100 : (cache->hit_count * 100 / total_count));
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch "positive: %u entries %llu bytes, "
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch "negative: %u entries %llu bytes",
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch cache_used = cache->max_size - cache->size_left;
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch "%"PRIuSIZE_T" bytes used of %"PRIuSIZE_T" bytes (%u%%)",
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch (unsigned int)(cache_used * 100ULL / cache->max_size));
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch /* reset counters */
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Boschstruct auth_cache *auth_cache_new(size_t max_size, unsigned int ttl_secs,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch hash_table_create(&cache->hash, default_pool, 0, str_hash, strcmp);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch lib_signals_set_handler(SIGHUP, LIBSIG_FLAGS_SAFE,
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch lib_signals_set_handler(SIGUSR2, LIBSIG_FLAGS_SAFE,
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainenvoid auth_cache_free(struct auth_cache **_cache)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch lib_signals_unset_handler(SIGHUP, sig_auth_cache_clear, cache);
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch lib_signals_unset_handler(SIGUSR2, sig_auth_cache_stats, cache);
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschunsigned int auth_cache_clear(struct auth_cache *cache)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch unsigned int ret = hash_table_count(cache->hash);
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschstatic bool auth_cache_node_is_user(struct auth_cache_node *node,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* The cache nodes begin with "P"/"U", passdb/userdb ID, "/" and
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch then usually followed by the username. It's too much trouble to
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch keep track of all the cache keys, so we'll just match it as if it
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch was the username. If e.g. '%n' is used in the cache key instead of
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch '%u', it means that cache entries can be removed only when @domain
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch isn't in the username parameter. */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return strncmp(data, username, username_len) == 0 &&
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch (data[username_len] == '\t' || data[username_len] == '\0');
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Boschstatic bool auth_cache_node_is_one_of_users(struct auth_cache_node *node,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch const char *const *usernames)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch unsigned int i;
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch if (auth_cache_node_is_user(node, usernames[i]))
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschunsigned int auth_cache_clear_users(struct auth_cache *cache,
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Bosch const char *const *usernames)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int ret = 0;
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch for (node = cache->tail; node != NULL; node = next) {
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen if (auth_cache_node_is_one_of_users(node, usernames)) {
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainenstatic const char *
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen const struct auth_request *auth_request ATTR_UNUSED)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch /* cache key %variables are separated by tabs, make sure that there
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen are no tabs in the string */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschstatic const char *
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschauth_request_expand_cache_key(const struct auth_request *request,
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen const char *key)
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch /* Uniquely identify the request's passdb/userdb with the P/U prefix
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch and by "%!", which expands to the passdb/userdb ID number. */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch key = t_strconcat(request->userdb_lookup ? "U" : "P", "%!/", key, NULL);
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch auth_request_get_var_expand_table(request, auth_cache_escape));
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Boschauth_cache_lookup(struct auth_cache *cache, const struct auth_request *request,
5a37824675033747fcae3fe3fc3c0dd7ef0ca1cdStephan Bosch const char *key, struct auth_cache_node **node_r,
return NULL;
return value;