passdb-cache.c revision d31c77e63713a6cf3687a4b38ff8daf6d6c7a3dd
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2004-2013 Dovecot authors, see the included COPYING file */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "auth-common.h"
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen#include "restrict-process-size.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "password-scheme.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "passdb.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "passdb-cache.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include <stdlib.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 }
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen auth_request_log_debug(request, "cache", "hit: %s", value);
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen}
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool passdb_cache_verify_plain(struct auth_request *request, const char *key,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen const char *password,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen enum passdb_result *result_r, int 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;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen bool expired, neg_expired;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen if (passdb_cache == NULL || key == NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* value = password \t ... */
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen value = auth_cache_lookup(passdb_cache, request, key, &node,
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen &expired, &neg_expired);
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen if (value == NULL || (expired && !use_expired)) {
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen auth_request_log_debug(request, "cache",
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen value == NULL ? "miss" : "expired");
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen }
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen passdb_cache_log_hit(request, value);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (*value == '\0') {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen /* negative cache entry */
ed3c4faf5ddf559818e4a3f7efc0f53f97396227Timo Sirainen auth_request_log_unknown_user(request, "cache");
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 */
bcf5f1acb2e3891f951fd0848c23b86c35efe7e1Timo Sirainen auth_request_log_info(request, "cache", "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,
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen scheme, "cache");
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;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen bool expired, neg_expired;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen if (passdb_cache == NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen value = auth_cache_lookup(passdb_cache, request, key, &node,
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen &expired, &neg_expired);
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen if (value == NULL || (expired && !use_expired)) {
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen auth_request_log_debug(request, "cache",
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen value == NULL ? "miss" : "expired");
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen }
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen passdb_cache_log_hit(request, value);
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}