passdb-cache.c revision 44cf91b7a701a9b4d9f59a990552eab4f7f64fbc
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2004-2016 Dovecot authors, see the included COPYING file */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "auth-common.h"
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen#include "restrict-process-size.h"
44cf91b7a701a9b4d9f59a990552eab4f7f64fbcTimo Sirainen#include "auth-request-stats.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "password-scheme.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "passdb.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "passdb-cache.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstruct auth_cache *passdb_cache = NULL;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainenstatic void
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainenpassdb_cache_log_hit(struct auth_request *request, const char *value)
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen{
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen const char *p;
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen if (!request->set->debug_passwords &&
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen *value != '\0' && *value != '\t') {
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen /* hide the password */
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen p = strchr(value, '\t');
6cbe2facd40ea3461620571a1c168ce9884be3b3Timo Sirainen value = t_strconcat(PASSWORD_HIDDEN_STR, p, NULL);
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen }
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_debug(request, AUTH_SUBSYS_DB, "cache hit: %s", value);
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen}
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainenstatic bool
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainenpassdb_cache_lookup(struct auth_request *request, const char *key,
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen bool use_expired, struct auth_cache_node **node_r,
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen const char **value_r, bool *neg_expired_r)
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen{
44cf91b7a701a9b4d9f59a990552eab4f7f64fbcTimo Sirainen struct auth_stats *stats = auth_request_stats_get(request);
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen const char *value;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen bool expired;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen /* value = password \t ... */
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen value = auth_cache_lookup(passdb_cache, request, key, node_r,
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen &expired, neg_expired_r);
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen if (value == NULL || (expired && !use_expired)) {
44cf91b7a701a9b4d9f59a990552eab4f7f64fbcTimo Sirainen stats->auth_cache_miss_count++;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen auth_request_log_debug(request, AUTH_SUBSYS_DB,
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen value == NULL ? "cache miss" :
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen "cache expired");
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen return FALSE;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen }
44cf91b7a701a9b4d9f59a990552eab4f7f64fbcTimo Sirainen stats->auth_cache_hit_count++;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen passdb_cache_log_hit(request, value);
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen *value_r = value;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen return TRUE;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen}
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool passdb_cache_verify_plain(struct auth_request *request, const char *key,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen const char *password,
5bb7c9863cbb62c41b13e7f42e04f1d57b4634f8Timo Sirainen enum passdb_result *result_r, bool use_expired)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen const char *value, *cached_pw, *scheme, *const *list;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen struct auth_cache_node *node;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen bool neg_expired;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen if (passdb_cache == NULL || key == NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen if (!passdb_cache_lookup(request, key, use_expired,
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen &node, &value, &neg_expired))
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (*value == '\0') {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* negative cache entry */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_unknown_user(request, AUTH_SUBSYS_DB);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen *result_r = PASSDB_RESULT_USER_UNKNOWN;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return TRUE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
3c296d819c54e21ce05c3d2eeeedc79be42ac593Timo Sirainen list = t_strsplit_tab(value);
3ab7783791bd46cdd46e9b9de3e98e8efcb6c6bfTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen cached_pw = list[0];
3ab7783791bd46cdd46e9b9de3e98e8efcb6c6bfTimo Sirainen if (*cached_pw == '\0') {
3ab7783791bd46cdd46e9b9de3e98e8efcb6c6bfTimo Sirainen /* NULL password */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(request, AUTH_SUBSYS_DB,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen "Cached NULL password access");
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen ret = 1;
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen } else {
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen scheme = password_get_scheme(&cached_pw);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen i_assert(scheme != NULL);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen ret = auth_request_password_verify(request, password, cached_pw,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen scheme, AUTH_SUBSYS_DB);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen if (ret == 0 && (node->last_success || neg_expired)) {
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen /* a) the last authentication was successful. assume
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen that the password was changed and cache is expired.
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen b) negative TTL reached, use it for password
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen mismatches too. */
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen node->last_success = FALSE;
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen return FALSE;
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen }
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen }
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen node->last_success = ret > 0;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen /* save the extra_fields only after we know we're using the
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen cached data */
553308791c097219e8eb31cbd03a29e9e1333848Timo Sirainen auth_request_set_fields(request, list + 1, NULL);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen *result_r = ret > 0 ? PASSDB_RESULT_OK :
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen PASSDB_RESULT_PASSWORD_MISMATCH;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return TRUE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool passdb_cache_lookup_credentials(struct auth_request *request,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen const char *key, const char **password_r,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen const char **scheme_r,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen enum passdb_result *result_r,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool use_expired)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen const char *value, *const *list;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen struct auth_cache_node *node;
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen bool neg_expired;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen if (passdb_cache == NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen if (!passdb_cache_lookup(request, key, use_expired,
90cf976e328e093da91a8332d96182201f4ef6c1Timo Sirainen &node, &value, &neg_expired))
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (*value == '\0') {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* negative cache entry */
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen *result_r = PASSDB_RESULT_USER_UNKNOWN;
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen *password_r = NULL;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen *scheme_r = NULL;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return TRUE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
3c296d819c54e21ce05c3d2eeeedc79be42ac593Timo Sirainen list = t_strsplit_tab(value);
553308791c097219e8eb31cbd03a29e9e1333848Timo Sirainen auth_request_set_fields(request, list + 1, NULL);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen *result_r = PASSDB_RESULT_OK;
7dcb5545370faa9d4ff83b3ede65a69fc3dd4b65Timo Sirainen *password_r = *list[0] == '\0' ? NULL : list[0];
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen *scheme_r = password_get_scheme(password_r);
cc0495b3bbe3c3e41c512274b302d6f0fa028187Timo Sirainen i_assert(*scheme_r != NULL || *password_r == NULL);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return TRUE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenvoid passdb_cache_init(const struct auth_settings *set)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen rlim_t limit;
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (set->cache_size == 0 || set->cache_ttl == 0)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen if (restrict_get_process_size(&limit) == 0 &&
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen set->cache_size > limit) {
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen i_warning("auth_cache_size (%luM) is higher than "
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen "process VSZ limit (%luM)",
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen (unsigned long)(set->cache_size/1024/1024),
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen (unsigned long)(limit/1024/1024));
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen }
61618d4c58080570f689614fec204ae14e90cef2Timo Sirainen passdb_cache = auth_cache_new(set->cache_size, set->cache_ttl,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen set->cache_negative_ttl);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenvoid passdb_cache_deinit(void)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
8ef7c24178fd798c3e0301c5b8afa1a9bdedd27fTimo Sirainen if (passdb_cache != NULL)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen auth_cache_free(&passdb_cache);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}