bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "auth-common.h"
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi#include "str.h"
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen#include "strescape.h"
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen#include "restrict-process-size.h"
44cf91b7a701a9b4d9f59a990552eab4f7f64fbcTimo Sirainen#include "auth-request-stats.h"
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi#include "auth-worker-server.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "password-scheme.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "passdb.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "passdb-cache.h"
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi#include "passdb-blocking.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
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomistatic bool passdb_cache_verify_plain_callback(const char *reply, void *context)
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi{
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi struct auth_request *request = context;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi enum passdb_result result;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi result = passdb_blocking_auth_worker_reply_parse(request, reply);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_verify_plain_callback_finish(result, request);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_unref(&request);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi return TRUE;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi}
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi
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;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_verify_plain_callback_finish(*result_r, request);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return TRUE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen list = t_strsplit_tabescaped(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;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi } else if (request->set->cache_verify_password_with_worker) {
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi string_t *str;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi str = t_str_new(128);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi str_printfa(str, "PASSW\t%u\t", request->passdb->passdb->id);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi str_append_tabescaped(str, password);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi str_append_c(str, '\t');
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi str_append_tabescaped(str, cached_pw);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi str_append_c(str, '\t');
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_export(request, str);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_log_debug(request, AUTH_SUBSYS_DB, "cache: "
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi "validating password on worker");
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_ref(request);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_worker_call(request->pool, request->user, str_c(str),
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi passdb_cache_verify_plain_callback, request);
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi return TRUE;
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen } else {
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen scheme = password_get_scheme(&cached_pw);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen i_assert(scheme != NULL);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen
ef4ca21b9195a2aca07a4965ea2d1d97c850b89amanuel ret = auth_request_password_verify_log(request, password, cached_pw,
ef4ca21b9195a2aca07a4965ea2d1d97c850b89amanuel scheme, AUTH_SUBSYS_DB,
ef4ca21b9195a2aca07a4965ea2d1d97c850b89amanuel !(node->last_success || neg_expired));
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;
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi
e42b74025f8d02ee7aa476897a3f44e25bb8fc10Aki Tuomi auth_request_verify_plain_callback_finish(*result_r, request);
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
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen list = t_strsplit_tabescaped(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) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi i_warning("auth_cache_size (%"PRIuUOFF_T"M) is higher than "
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen "process VSZ limit (%luM)",
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi 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}