auth-request.c revision bd0720cca74a9f7603675b01c17bbea7945c1bc4
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "auth-common.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "ioloop.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "buffer.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "hash.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "sha1.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "hex-binary.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "str.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "safe-memset.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "str-sanitize.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "strescape.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "var-expand.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "auth-cache.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "auth-request.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "auth-request-handler.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "auth-client-connection.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "auth-master-connection.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "passdb.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "passdb-blocking.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "userdb-blocking.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "passdb-cache.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "password-scheme.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include <stdlib.h>
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include <sys/stat.h>
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define CACHED_PASSWORD_SCHEME "SHA1"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterunsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX];
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void get_log_prefix(string_t *str, struct auth_request *auth_request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *subsystem);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct auth_request *
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterauth_request_new(const struct mech_module *mech)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_request *request;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request = mech->auth_new();
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->state = AUTH_REQUEST_STATE_NEW;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_state_count[AUTH_REQUEST_STATE_NEW]++;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->refcount = 1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->last_access = ioloop_time;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->set = global_auth_settings;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech = mech;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech_name = mech == NULL ? NULL : mech->mech_name;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return request;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct auth_request *auth_request_new_dummy(void)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_request *request;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter pool_t pool;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter pool = pool_alloconly_create("auth_request", 1024);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request = p_new(pool, struct auth_request, 1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->pool = pool;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->state = AUTH_REQUEST_STATE_NEW;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_state_count[AUTH_REQUEST_STATE_NEW]++;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->refcount = 1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->last_access = ioloop_time;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->set = global_auth_settings;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return request;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_set_state(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter enum auth_request_state state)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->state == state)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(auth_request_state_count[request->state] > 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_state_count[request->state]--;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_state_count[state]++;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->state = state;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_refresh_proctitle();
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_init(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth *auth;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth = auth_request_get_auth(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->set = auth->set;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb = auth->passdbs;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->userdb = auth->userdbs;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct auth *auth_request_get_auth(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return auth_find_service(request->service);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_success(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const void *data, size_t data_size)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->passdb_failure) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* password was valid, but some other check failed. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_fail(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->successful = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_refresh_last_access(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_handler_reply(request, AUTH_CLIENT_RESULT_SUCCESS,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter data, data_size);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_fail(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_refresh_last_access(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_handler_reply(request, AUTH_CLIENT_RESULT_FAILURE,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter NULL, 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_internal_failure(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->internal_failure = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_fail(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_ref(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->refcount++;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_unref(struct auth_request **_request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_request *request = *_request;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *_request = NULL;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->refcount > 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (--request->refcount > 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_state_count[request->state]--;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_refresh_proctitle();
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->to_abort != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter timeout_remove(&request->to_abort);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->to_penalty != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter timeout_remove(&request->to_penalty);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->mech != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech->auth_free(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter pool_unref(&request->pool);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_export(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_stream_reply *reply)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "user", request->user);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "service", request->service);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->master_user != NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "master_user",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->master_user);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "original_username",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->original_username);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "requested_login_user",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->requested_login_user);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->local_ip.family != 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "lip",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter net_ip2addr(&request->local_ip));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->remote_ip.family != 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "rip",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter net_ip2addr(&request->remote_ip));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->local_port != 0) {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter auth_stream_reply_add(reply, "lport",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter dec2str(request->local_port));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->remote_port != 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "rport",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter dec2str(request->remote_port));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->secured)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "secured", "1");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->skip_password_check)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "skip_password_check", "1");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->valid_client_cert)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "valid-client-cert", "1");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->no_penalty)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "no-penalty", "1");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->successful)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "successful", "1");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->mech_name != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(reply, "mech", request->mech_name);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterbool auth_request_import(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *key, const char *value)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (strcmp(key, "user") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->user = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "master_user") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->master_user = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "original_username") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->original_username = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "requested_login_user") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->requested_login_user = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "cert_username") == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->set->ssl_username_from_cert) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* get username from SSL certificate. it overrides
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter the username given by the auth mechanism. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->user = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->cert_username = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (strcmp(key, "service") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->service = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "lip") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter net_addr2ip(value, &request->local_ip);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "rip") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter net_addr2ip(value, &request->remote_ip);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "lport") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->local_port = atoi(value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "rport") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->remote_port = atoi(value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "secured") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->secured = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "nologin") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->no_login = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "valid-client-cert") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->valid_client_cert = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "no-penalty") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->no_penalty = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "successful") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->successful = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else if (strcmp(key, "skip_password_check") == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->master_user != NULL);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->skip_password_check = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (strcmp(key, "mech") == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech_name = p_strdup(request->pool, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return TRUE;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_initial(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->state == AUTH_REQUEST_STATE_NEW);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_set_state(request, AUTH_REQUEST_STATE_MECH_CONTINUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech->auth_initial(request, request->initial_response,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->initial_response_len);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter}
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_continue(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const unsigned char *data, size_t data_size)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_refresh_last_access(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech->auth_continue(request, data, data_size);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void auth_request_save_cache(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter enum passdb_result result)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct passdb_module *passdb = request->passdb->passdb;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *extra_fields, *encoded_password;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter string_t *str;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (result) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case PASSDB_RESULT_USER_UNKNOWN:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case PASSDB_RESULT_PASSWORD_MISMATCH:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case PASSDB_RESULT_OK:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case PASSDB_RESULT_SCHEME_NOT_AVAILABLE:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* can be cached */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter break;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case PASSDB_RESULT_USER_DISABLED:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case PASSDB_RESULT_PASS_EXPIRED:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* FIXME: we can't cache this now, or cache lookup would
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter return success. */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter return;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case PASSDB_RESULT_INTERNAL_FAILURE:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter i_unreached();
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter extra_fields = request->extra_fields == NULL ? NULL :
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter auth_stream_reply_export(request->extra_fields);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (passdb_cache == NULL || passdb->cache_key == NULL ||
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter request->master_user != NULL)
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter return;
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (result < 0) {
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter /* lookup failed. */
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (result == PASSDB_RESULT_USER_UNKNOWN) {
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter auth_cache_insert(passdb_cache, request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter passdb->cache_key, "", FALSE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!request->no_password && request->passdb_password == NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* passdb didn't provide the correct password */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (result != PASSDB_RESULT_OK ||
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->mech_password == NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter /* we can still cache valid password lookups though.
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter strdup() it so that mech_password doesn't get
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter cleared too early. */
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (!password_generate_encoded(request->mech_password,
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter request->user,
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter CACHED_PASSWORD_SCHEME,
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter &encoded_password))
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter i_unreached();
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter request->passdb_password =
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter p_strconcat(request->pool, "{"CACHED_PASSWORD_SCHEME"}",
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter encoded_password, NULL);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* save all except the currently given password in cache */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter str = t_str_new(256);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (request->passdb_password != NULL) {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (*request->passdb_password != '{') {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* cached passwords must have a known scheme */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter str_append_c(str, '{');
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter str_append(str, passdb->default_pass_scheme);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter str_append_c(str, '}');
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (strchr(request->passdb_password, '\t') != NULL)
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter i_panic("%s: Password contains TAB", request->user);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (strchr(request->passdb_password, '\n') != NULL)
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter i_panic("%s: Password contains LF", request->user);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter str_append(str, request->passdb_password);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (extra_fields != NULL && *extra_fields != '\0') {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str_append_c(str, '\t');
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str_append(str, extra_fields);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->extra_cache_fields != NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter extra_fields =
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_export(request->extra_cache_fields);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*extra_fields != '\0') {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str_append_c(str, '\t');
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str_append(str, extra_fields);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str),
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter result == PASSDB_RESULT_OK);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void auth_request_userdb_reply_update_user(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *str, *p;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str = t_strdup(auth_stream_reply_export(request->userdb_reply));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* reset the reply and add the new username */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_reset(request->userdb_reply);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(request->userdb_reply, NULL, request->user);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* add the rest */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter p = strchr(str, '\t');
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (p != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_import(request->userdb_reply, p + 1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool auth_request_master_lookup_finish(struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_passdb *passdb;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->passdb_failure)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* master login successful. update user and master_user variables. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_log_info(request, "passdb", "Master user logging in as %s",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->requested_login_user);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->master_user = request->user;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->user = request->requested_login_user;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->requested_login_user = NULL;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->userdb_reply != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_userdb_reply_update_user(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->skip_password_check = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb_password = NULL;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!request->passdb->set->pass) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* skip the passdb lookup, we're authenticated now. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* the authentication continues with passdb lookup for the
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter requested_login_user. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb = auth_request_get_auth(request)->passdbs;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (passdb = request->passdb; passdb != NULL; passdb = passdb->next) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (passdb->passdb->iface.lookup_credentials != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (passdb == NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_log_error(request, "passdb",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "No passdbs support skipping password verification - "
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "pass=yes can't be used in master passdb");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterauth_request_handle_passdb_callback(enum passdb_result *result,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->passdb_password != NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter safe_memset(request->passdb_password, 0,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter strlen(request->passdb_password));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->passdb->set->deny &&
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *result != PASSDB_RESULT_USER_UNKNOWN) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* deny passdb. we can get through this step only if the
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter lookup returned that user doesn't exist in it. internal
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter errors are fatal here. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*result != PASSDB_RESULT_INTERNAL_FAILURE) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_log_info(request, "passdb",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "User found from deny passdb");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *result = PASSDB_RESULT_USER_DISABLED;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (*result == PASSDB_RESULT_OK) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* success */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->requested_login_user != NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* this was a master user lookup. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!auth_request_master_lookup_finish(request))
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->passdb->set->pass) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* this wasn't the final passdb lookup,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter continue to next passdb */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb = request->passdb->next;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb_password = NULL;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (*result == PASSDB_RESULT_PASS_EXPIRED) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->extra_fields == NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->extra_fields =
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_init(request->pool);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_add(request->extra_fields, "reason",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "Password expired");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (request->passdb->next != NULL &&
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *result != PASSDB_RESULT_USER_DISABLED) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* try next passdb. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb = request->passdb->next;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb_password = NULL;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*result == PASSDB_RESULT_USER_UNKNOWN) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* remember that we did at least one successful
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter passdb lookup */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb_user_unknown = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (*result == PASSDB_RESULT_INTERNAL_FAILURE) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* remember that we have had an internal failure. at
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter the end return internal failure if we couldn't
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter successfully login. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->passdb_internal_failure = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->extra_fields != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_stream_reply_reset(request->extra_fields);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (request->passdb_internal_failure) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* last passdb lookup returned internal failure. it may have
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter had the correct password, so return internal failure
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter instead of plain failure. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *result = PASSDB_RESULT_INTERNAL_FAILURE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterauth_request_verify_plain_callback_finish(enum passdb_result result,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!auth_request_handle_passdb_callback(&result, request)) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* try next passdb */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_verify_plain(request, request->mech_password,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->private_callback.verify_plain);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_ref(request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request->private_callback.verify_plain(result, request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter safe_memset(request->mech_password, 0,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter strlen(request->mech_password));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_unref(&request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_verify_plain_callback(enum passdb_result result,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct auth_request *request)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->state == AUTH_REQUEST_STATE_PASSDB);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_set_state(request, AUTH_REQUEST_STATE_MECH_CONTINUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (result != PASSDB_RESULT_INTERNAL_FAILURE)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_save_cache(request, result);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* lookup failed. if we're looking here only because the
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter request was expired in cache, fallback to using cached
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter expired record. */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter const char *cache_key = request->passdb->passdb->cache_key;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (passdb_cache_verify_plain(request, cache_key,
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter request->mech_password,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter &result, TRUE)) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_log_info(request, "passdb",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "Fallbacking to expired data from cache");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter auth_request_verify_plain_callback_finish(result, request);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool password_has_illegal_chars(const char *password)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (; *password != '\0'; password++) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (*password) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case '\001':
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case '\t':
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case '\r':
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case '\n':
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* these characters have a special meaning in internal
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter protocols, make sure the password doesn't
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter accidentally get there unescaped. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid auth_request_verify_plain(struct auth_request *request,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *password,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter verify_plain_callback_t *callback)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct passdb_module *passdb;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter enum passdb_result result;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *cache_key;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (request->passdb == NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* no masterdbs, master logins not supported */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(request->requested_login_user != NULL);
auth_request_log_info(request, "passdb",
"Attempted master login with no master passdbs "
"(trying to log in as user: %s)",
request->requested_login_user);
callback(PASSDB_RESULT_USER_UNKNOWN, request);
return;
}
if (password_has_illegal_chars(password)) {
auth_request_log_info(request, "passdb",
"Attempted login with password having illegal chars");
callback(PASSDB_RESULT_USER_UNKNOWN, request);
return;
}
passdb = request->passdb->passdb;
if (request->mech_password == NULL)
request->mech_password = p_strdup(request->pool, password);
else
i_assert(request->mech_password == password);
request->private_callback.verify_plain = callback;
cache_key = passdb_cache == NULL ? NULL : passdb->cache_key;
if (passdb_cache_verify_plain(request, cache_key, password,
&result, FALSE)) {
auth_request_verify_plain_callback_finish(result, request);
return;
}
auth_request_set_state(request, AUTH_REQUEST_STATE_PASSDB);
request->credentials_scheme = NULL;
if (passdb->iface.verify_plain == NULL) {
/* we're deinitializing and just want to get rid of this
request */
auth_request_verify_plain_callback(
PASSDB_RESULT_INTERNAL_FAILURE, request);
} else if (passdb->blocking) {
passdb_blocking_verify_plain(request);
} else if (passdb->iface.verify_plain != NULL) {
passdb->iface.verify_plain(request, password,
auth_request_verify_plain_callback);
}
}
static void
auth_request_lookup_credentials_finish(enum passdb_result result,
const unsigned char *credentials,
size_t size,
struct auth_request *request)
{
if (!auth_request_handle_passdb_callback(&result, request)) {
/* try next passdb */
auth_request_lookup_credentials(request,
request->credentials_scheme,
request->private_callback.lookup_credentials);
} else {
if (request->set->debug_passwords &&
result == PASSDB_RESULT_OK) {
auth_request_log_debug(request, "password",
"Credentials: %s",
binary_to_hex(credentials, size));
}
if (result == PASSDB_RESULT_SCHEME_NOT_AVAILABLE &&
request->passdb_user_unknown) {
/* one of the passdbs accepted the scheme,
but the user was unknown there */
result = PASSDB_RESULT_USER_UNKNOWN;
}
request->private_callback.
lookup_credentials(result, credentials, size, request);
}
}
void auth_request_lookup_credentials_callback(enum passdb_result result,
const unsigned char *credentials,
size_t size,
struct auth_request *request)
{
const char *cache_cred, *cache_scheme;
i_assert(request->state == AUTH_REQUEST_STATE_PASSDB);
auth_request_set_state(request, AUTH_REQUEST_STATE_MECH_CONTINUE);
if (result != PASSDB_RESULT_INTERNAL_FAILURE)
auth_request_save_cache(request, result);
else {
/* lookup failed. if we're looking here only because the
request was expired in cache, fallback to using cached
expired record. */
const char *cache_key = request->passdb->passdb->cache_key;
if (passdb_cache_lookup_credentials(request, cache_key,
&cache_cred, &cache_scheme,
&result, TRUE)) {
auth_request_log_info(request, "passdb",
"Fallbacking to expired data from cache");
passdb_handle_credentials(
result, cache_cred, cache_scheme,
auth_request_lookup_credentials_finish,
request);
return;
}
}
auth_request_lookup_credentials_finish(result, credentials, size,
request);
}
void auth_request_lookup_credentials(struct auth_request *request,
const char *scheme,
lookup_credentials_callback_t *callback)
{
struct passdb_module *passdb = request->passdb->passdb;
const char *cache_key, *cache_cred, *cache_scheme;
enum passdb_result result;
i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
request->credentials_scheme = p_strdup(request->pool, scheme);
request->private_callback.lookup_credentials = callback;
cache_key = passdb_cache == NULL ? NULL : passdb->cache_key;
if (cache_key != NULL) {
if (passdb_cache_lookup_credentials(request, cache_key,
&cache_cred, &cache_scheme,
&result, FALSE)) {
passdb_handle_credentials(
result, cache_cred, cache_scheme,
auth_request_lookup_credentials_finish,
request);
return;
}
}
auth_request_set_state(request, AUTH_REQUEST_STATE_PASSDB);
if (passdb->iface.lookup_credentials == NULL) {
/* this passdb doesn't support credentials */
auth_request_log_debug(request, "password",
"passdb doesn't support credential lookups");
auth_request_lookup_credentials_callback(
PASSDB_RESULT_SCHEME_NOT_AVAILABLE, NULL, 0, request);
} else if (passdb->blocking) {
passdb_blocking_lookup_credentials(request);
} else {
passdb->iface.lookup_credentials(request,
auth_request_lookup_credentials_callback);
}
}
void auth_request_set_credentials(struct auth_request *request,
const char *scheme, const char *data,
set_credentials_callback_t *callback)
{
struct passdb_module *passdb = request->passdb->passdb;
const char *cache_key, *new_credentials;
cache_key = passdb_cache == NULL ? NULL : passdb->cache_key;
if (cache_key != NULL)
auth_cache_remove(passdb_cache, request, cache_key);
request->private_callback.set_credentials = callback;
new_credentials = t_strdup_printf("{%s}%s", scheme, data);
if (passdb->blocking)
passdb_blocking_set_credentials(request, new_credentials);
else if (passdb->iface.set_credentials != NULL) {
passdb->iface.set_credentials(request, new_credentials,
callback);
} else {
/* this passdb doesn't support credentials update */
callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
}
}
static void auth_request_userdb_save_cache(struct auth_request *request,
enum userdb_result result)
{
struct userdb_module *userdb = request->userdb->userdb;
const char *str;
if (passdb_cache == NULL || userdb->cache_key == NULL ||
request->master_user != NULL)
return;
str = result == USERDB_RESULT_USER_UNKNOWN ? "" :
auth_stream_reply_export(request->userdb_reply);
/* last_success has no meaning with userdb */
auth_cache_insert(passdb_cache, request, userdb->cache_key, str, FALSE);
}
static bool auth_request_lookup_user_cache(struct auth_request *request,
const char *key,
struct auth_stream_reply **reply_r,
enum userdb_result *result_r,
bool use_expired)
{
const char *value;
struct auth_cache_node *node;
bool expired, neg_expired;
if (request->master_user != NULL)
return FALSE;
value = auth_cache_lookup(passdb_cache, request, key, &node,
&expired, &neg_expired);
if (value == NULL || (expired && !use_expired)) {
auth_request_log_debug(request, "userdb-cache",
value == NULL ? "miss" : "expired");
return FALSE;
}
auth_request_log_debug(request, "userdb-cache", "hit: %s", value);
if (*value == '\0') {
/* negative cache entry */
*result_r = USERDB_RESULT_USER_UNKNOWN;
*reply_r = auth_stream_reply_init(request->pool);
return TRUE;
}
*result_r = USERDB_RESULT_OK;
*reply_r = auth_stream_reply_init(request->pool);
auth_stream_reply_import(*reply_r, value);
return TRUE;
}
void auth_request_userdb_callback(enum userdb_result result,
struct auth_request *request)
{
struct userdb_module *userdb = request->userdb->userdb;
if (result != USERDB_RESULT_OK && request->userdb->next != NULL) {
/* try next userdb. */
if (result == USERDB_RESULT_INTERNAL_FAILURE)
request->userdb_internal_failure = TRUE;
request->userdb = request->userdb->next;
auth_request_lookup_user(request,
request->private_callback.userdb);
return;
}
if (request->userdb_internal_failure && result != USERDB_RESULT_OK) {
/* one of the userdb lookups failed. the user might have been
in there, so this is an internal failure */
result = USERDB_RESULT_INTERNAL_FAILURE;
} else if (result == USERDB_RESULT_USER_UNKNOWN &&
request->client_pid != 0) {
/* this was an actual login attempt, the user should
have been found. */
if (auth_request_get_auth(request)->userdbs->next == NULL) {
auth_request_log_error(request, "userdb",
"user not found from userdb %s",
request->userdb->userdb->iface->name);
} else {
auth_request_log_error(request, "userdb",
"user not found from any userdbs");
}
}
if (request->userdb_lookup_failed) {
/* no caching */
} else if (result != USERDB_RESULT_INTERNAL_FAILURE)
auth_request_userdb_save_cache(request, result);
else if (passdb_cache != NULL && userdb->cache_key != NULL) {
/* lookup failed. if we're looking here only because the
request was expired in cache, fallback to using cached
expired record. */
const char *cache_key = userdb->cache_key;
struct auth_stream_reply *reply;
if (auth_request_lookup_user_cache(request, cache_key, &reply,
&result, TRUE)) {
request->userdb_reply = reply;
auth_request_log_info(request, "userdb",
"Fallbacking to expired data from cache");
}
}
request->private_callback.userdb(result, request);
}
void auth_request_lookup_user(struct auth_request *request,
userdb_callback_t *callback)
{
struct userdb_module *userdb = request->userdb->userdb;
const char *cache_key;
request->private_callback.userdb = callback;
request->userdb_lookup = TRUE;
/* (for now) auth_cache is shared between passdb and userdb */
cache_key = passdb_cache == NULL ? NULL : userdb->cache_key;
if (cache_key != NULL) {
struct auth_stream_reply *reply;
enum userdb_result result;
if (auth_request_lookup_user_cache(request, cache_key, &reply,
&result, FALSE)) {
request->userdb_reply = reply;
request->private_callback.userdb(result, request);
return;
}
}
if (userdb->iface->lookup == NULL) {
/* we are deinitializing */
auth_request_userdb_callback(USERDB_RESULT_INTERNAL_FAILURE,
request);
} else if (userdb->blocking)
userdb_blocking_lookup(request);
else
userdb->iface->lookup(request, auth_request_userdb_callback);
}
static char *
auth_request_fix_username(struct auth_request *request, const char *username,
const char **error_r)
{
const struct auth_settings *set = request->set;
unsigned char *p;
char *user;
if (*set->default_realm != '\0' &&
strchr(username, '@') == NULL) {
user = p_strconcat(request->pool, username, "@",
set->default_realm, NULL);
} else {
user = p_strdup(request->pool, username);
}
for (p = (unsigned char *)user; *p != '\0'; p++) {
if (set->username_translation_map[*p & 0xff] != 0)
*p = set->username_translation_map[*p & 0xff];
if (set->username_chars_map[*p & 0xff] == 0) {
*error_r = t_strdup_printf(
"Username character disallowed by auth_username_chars: "
"0x%02x (username: %s)", *p,
str_sanitize(username, 128));
return NULL;
}
}
if (*set->username_format != '\0') {
/* username format given, put it through variable expansion.
we'll have to temporarily replace request->user to get
%u to be the wanted username */
const struct var_expand_table *table;
char *old_username;
string_t *dest;
old_username = request->user;
request->user = user;
dest = t_str_new(256);
table = auth_request_get_var_expand_table(request, NULL);
var_expand(dest, set->username_format, table);
user = p_strdup(request->pool, str_c(dest));
request->user = old_username;
}
return user;
}
bool auth_request_set_username(struct auth_request *request,
const char *username, const char **error_r)
{
const struct auth_settings *set = request->set;
const char *p, *login_username = NULL;
if (*set->master_user_separator != '\0' && !request->userdb_lookup) {
/* check if the username contains a master user */
p = strchr(username, *set->master_user_separator);
if (p != NULL) {
/* it does, set it. */
login_username = t_strdup_until(username, p);
if (*login_username == '\0') {
*error_r = "Empty login username";
return FALSE;
}
/* username is the master user */
username = p + 1;
}
}
if (request->original_username == NULL) {
/* the username may change later, but we need to use this
username when verifying at least DIGEST-MD5 password. */
request->original_username = p_strdup(request->pool, username);
}
if (request->cert_username) {
/* cert_username overrides the username given by
authentication mechanism. but still do checks and
translations to it. */
username = request->user;
}
if (*username == '\0') {
/* Some PAM plugins go nuts with empty usernames */
*error_r = "Empty username";
return FALSE;
}
request->user = auth_request_fix_username(request, username, error_r);
if (request->user == NULL)
return FALSE;
if (request->translated_username == NULL) {
/* similar to original_username, but after translations */
request->translated_username = request->user;
}
if (login_username != NULL) {
if (!auth_request_set_login_username(request,
login_username,
error_r))
return FALSE;
}
return TRUE;
}
bool auth_request_set_login_username(struct auth_request *request,
const char *username,
const char **error_r)
{
i_assert(*username != '\0');
if (strcmp(username, request->user) == 0) {
/* The usernames are the same, we don't really wish to log
in as someone else */
return TRUE;
}
/* lookup request->user from masterdb first */
request->passdb = auth_request_get_auth(request)->masterdbs;
request->requested_login_user =
auth_request_fix_username(request, username, error_r);
if (request->requested_login_user == NULL)
return FALSE;
auth_request_log_debug(request, "auth",
"Master user lookup for login: %s",
request->requested_login_user);
return TRUE;
}
static void auth_request_validate_networks(struct auth_request *request,
const char *networks)
{
const char *const *net;
struct ip_addr net_ip;
unsigned int bits;
bool found = FALSE;
if (request->remote_ip.family == 0) {
/* IP not known */
auth_request_log_info(request, "passdb",
"allow_nets check failed: Remote IP not known");
request->passdb_failure = TRUE;
return;
}
for (net = t_strsplit_spaces(networks, ", "); *net != NULL; net++) {
auth_request_log_debug(request, "auth",
"allow_nets: Matching for network %s", *net);
if (net_parse_range(*net, &net_ip, &bits) < 0) {
auth_request_log_info(request, "passdb",
"allow_nets: Invalid network '%s'", *net);
}
if (net_is_in_network(&request->remote_ip, &net_ip, bits)) {
found = TRUE;
break;
}
}
if (!found) {
auth_request_log_info(request, "passdb",
"allow_nets check failed: IP not in allowed networks");
}
request->passdb_failure = !found;
}
static void
auth_request_set_password(struct auth_request *request, const char *value,
const char *default_scheme, bool noscheme)
{
if (request->passdb_password != NULL) {
auth_request_log_error(request,
request->passdb->passdb->iface.name,
"Multiple password values not supported");
return;
}
/* if the password starts with '{' it most likely contains
also '}'. check it anyway to make sure, because we
assert-crash later if it doesn't exist. this could happen
if plaintext passwords are used. */
if (*value == '{' && !noscheme && strchr(value, '}') != NULL)
request->passdb_password = p_strdup(request->pool, value);
else {
i_assert(default_scheme != NULL);
request->passdb_password =
p_strdup_printf(request->pool, "{%s}%s",
default_scheme, value);
}
}
static void auth_request_set_reply_field(struct auth_request *request,
const char *name, const char *value)
{
if (strcmp(name, "nologin") == 0) {
/* user can't actually login - don't keep this
reply for master */
request->no_login = TRUE;
value = NULL;
} else if (strcmp(name, "proxy") == 0) {
/* we're proxying authentication for this user. send
password back if using plaintext authentication. */
request->proxy = TRUE;
value = NULL;
} else if (strcmp(name, "proxy_maybe") == 0) {
/* like "proxy", but log in normally if we're proxying to
ourself */
request->proxy = TRUE;
request->proxy_maybe = TRUE;
value = NULL;
}
if (request->extra_fields == NULL)
request->extra_fields = auth_stream_reply_init(request->pool);
auth_stream_reply_add(request->extra_fields, name, value);
}
static const char *
get_updated_username(const char *old_username,
const char *name, const char *value)
{
const char *p;
if (strcmp(name, "user") == 0) {
/* replace the whole username */
return value;
}
p = strchr(old_username, '@');
if (strcmp(name, "username") == 0) {
if (strchr(value, '@') != NULL)
return value;
/* preserve the current @domain */
return t_strconcat(value, p, NULL);
}
if (strcmp(name, "domain") == 0) {
if (p == NULL) {
/* add the domain */
return t_strconcat(old_username, "@", value, NULL);
} else {
/* replace the existing domain */
p = t_strdup_until(old_username, p + 1);
return t_strconcat(p, value, NULL);
}
}
return NULL;
}
static bool
auth_request_try_update_username(struct auth_request *request,
const char *name, const char *value)
{
const char *new_value;
new_value = get_updated_username(request->user, name, value);
if (new_value == NULL)
return FALSE;
if (strcmp(request->user, new_value) != 0) {
auth_request_log_debug(request, "auth",
"username changed %s -> %s",
request->user, new_value);
request->user = p_strdup(request->pool, new_value);
if (request->userdb_reply != NULL)
auth_request_userdb_reply_update_user(request);
}
return TRUE;
}
void auth_request_set_field(struct auth_request *request,
const char *name, const char *value,
const char *default_scheme)
{
i_assert(*name != '\0');
i_assert(value != NULL);
if (strcmp(name, "password") == 0) {
auth_request_set_password(request, value,
default_scheme, FALSE);
return;
}
if (strcmp(name, "password_noscheme") == 0) {
auth_request_set_password(request, value, default_scheme, TRUE);
return;
}
if (auth_request_try_update_username(request, name, value)) {
/* don't change the original value so it gets saved correctly
to cache. */
} else if (strcmp(name, "nodelay") == 0) {
/* don't delay replying to client of the failure */
request->no_failure_delay = TRUE;
} else if (strcmp(name, "nopassword") == 0) {
/* NULL password - anything goes */
const char *password = request->passdb_password;
if (password != NULL) {
(void)password_get_scheme(&password);
if (*password != '\0') {
auth_request_log_error(request,
request->passdb->passdb->iface.name,
"nopassword set but password is "
"non-empty");
return;
}
}
request->no_password = TRUE;
request->passdb_password = NULL;
} else if (strcmp(name, "allow_nets") == 0) {
auth_request_validate_networks(request, value);
} else if (strncmp(name, "userdb_", 7) == 0) {
/* for prefetch userdb */
if (request->userdb_reply == NULL)
auth_request_init_userdb_reply(request);
auth_request_set_userdb_field(request, name + 7, value);
} else {
/* these fields are returned to client */
auth_request_set_reply_field(request, name, value);
return;
}
if ((passdb_cache != NULL &&
request->passdb->passdb->cache_key != NULL) || worker) {
/* we'll need to get this field stored into cache,
or we're a worker and we'll need to send this to the main
auth process that can store it in the cache. */
if (request->extra_cache_fields == NULL) {
request->extra_cache_fields =
auth_stream_reply_init(request->pool);
}
auth_stream_reply_add(request->extra_cache_fields, name, value);
}
}
void auth_request_set_fields(struct auth_request *request,
const char *const *fields,
const char *default_scheme)
{
const char *key, *value;
for (; *fields != NULL; fields++) {
if (**fields == '\0')
continue;
value = strchr(*fields, '=');
if (value == NULL) {
key = *fields;
value = "";
} else {
key = t_strdup_until(*fields, value);
value++;
}
auth_request_set_field(request, key, value, default_scheme);
}
}
void auth_request_init_userdb_reply(struct auth_request *request)
{
request->userdb_reply = auth_stream_reply_init(request->pool);
auth_stream_reply_add(request->userdb_reply, NULL, request->user);
}
static void auth_request_set_uidgid_file(struct auth_request *request,
const char *path_template)
{
string_t *path;
struct stat st;
path = t_str_new(256);
var_expand(path, path_template,
auth_request_get_var_expand_table(request, NULL));
if (stat(str_c(path), &st) < 0) {
auth_request_log_error(request, "uidgid_file",
"stat(%s) failed: %m", str_c(path));
} else {
auth_stream_reply_add(request->userdb_reply,
"uid", dec2str(st.st_uid));
auth_stream_reply_add(request->userdb_reply,
"gid", dec2str(st.st_gid));
}
}
void auth_request_set_userdb_field(struct auth_request *request,
const char *name, const char *value)
{
uid_t uid;
gid_t gid;
if (strcmp(name, "uid") == 0) {
uid = userdb_parse_uid(request, value);
if (uid == (uid_t)-1) {
request->userdb_lookup_failed = TRUE;
return;
}
value = dec2str(uid);
} else if (strcmp(name, "gid") == 0) {
gid = userdb_parse_gid(request, value);
if (gid == (gid_t)-1) {
request->userdb_lookup_failed = TRUE;
return;
}
value = dec2str(gid);
} else if (strcmp(name, "tempfail") == 0) {
request->userdb_lookup_failed = TRUE;
return;
} else if (auth_request_try_update_username(request, name, value)) {
return;
} else if (strcmp(name, "uidgid_file") == 0) {
auth_request_set_uidgid_file(request, value);
return;
} else if (strcmp(name, "userdb_import") == 0) {
auth_stream_reply_import(request->userdb_reply, value);
return;
} else if (strcmp(name, "system_user") == 0) {
/* FIXME: the system_user is for backwards compatibility */
name = "system_groups_user";
}
auth_stream_reply_add(request->userdb_reply, name, value);
}
void auth_request_set_userdb_field_values(struct auth_request *request,
const char *name,
const char *const *values)
{
if (*values == NULL)
return;
if (strcmp(name, "gid") == 0) {
/* convert gids to comma separated list */
string_t *value;
gid_t gid;
value = t_str_new(128);
for (; *values != NULL; values++) {
gid = userdb_parse_gid(request, *values);
if (gid == (gid_t)-1) {
request->userdb_lookup_failed = TRUE;
return;
}
if (str_len(value) > 0)
str_append_c(value, ',');
str_append(value, dec2str(gid));
}
auth_stream_reply_add(request->userdb_reply, name,
str_c(value));
} else {
/* add only one */
if (values[1] != NULL) {
auth_request_log_warning(request, "userdb",
"Multiple values found for '%s', "
"using value '%s'", name, *values);
}
auth_request_set_userdb_field(request, name, *values);
}
}
static bool auth_request_proxy_is_self(struct auth_request *request)
{
const char *const *tmp, *host = NULL, *port = NULL, *destuser = NULL;
struct ip_addr ip;
tmp = auth_stream_split(request->extra_fields);
for (; *tmp != NULL; tmp++) {
if (strncmp(*tmp, "host=", 5) == 0)
host = *tmp + 5;
else if (strncmp(*tmp, "port=", 5) == 0)
port = *tmp + 5;
if (strncmp(*tmp, "destuser=", 9) == 0)
destuser = *tmp + 9;
}
if (host == NULL || net_addr2ip(host, &ip) < 0) {
/* broken setup */
return FALSE;
}
if (!net_ip_compare(&ip, &request->local_ip))
return FALSE;
if (port != NULL && !str_uint_equals(port, request->local_port))
return FALSE;
return destuser == NULL ||
strcmp(destuser, request->original_username) == 0;
}
void auth_request_proxy_finish(struct auth_request *request, bool success)
{
if (!request->proxy || request->no_login)
return;
if (!success) {
/* drop all proxy fields */
} else if (!request->proxy_maybe) {
/* proxying */
request->no_login = TRUE;
return;
} else if (!auth_request_proxy_is_self(request)) {
/* proxy destination isn't ourself - proxy */
auth_stream_reply_remove(request->extra_fields, "proxy_maybe");
auth_stream_reply_add(request->extra_fields, "proxy", NULL);
request->no_login = TRUE;
return;
} else {
/* proxying to ourself - log in without proxying by dropping
all the proxying fields. */
}
auth_stream_reply_remove(request->extra_fields, "proxy");
auth_stream_reply_remove(request->extra_fields, "proxy_maybe");
auth_stream_reply_remove(request->extra_fields, "host");
auth_stream_reply_remove(request->extra_fields, "port");
auth_stream_reply_remove(request->extra_fields, "destuser");
}
static void log_password_failure(struct auth_request *request,
const char *plain_password,
const char *crypted_password,
const char *scheme, const char *user,
const char *subsystem)
{
static bool scheme_ok = FALSE;
string_t *str = t_str_new(256);
const char *working_scheme;
str_printfa(str, "%s(%s) != '%s'", scheme,
plain_password, crypted_password);
if (!scheme_ok) {
/* perhaps the scheme is wrong - see if we can find
a working one */
working_scheme = password_scheme_detect(plain_password,
crypted_password, user);
if (working_scheme != NULL) {
str_printfa(str, ", try %s scheme instead",
working_scheme);
}
}
auth_request_log_debug(request, subsystem, "%s", str_c(str));
}
void auth_request_log_password_mismatch(struct auth_request *request,
const char *subsystem)
{
string_t *str;
const char *log_type = request->set->verbose_passwords;
if (strcmp(log_type, "no") == 0) {
auth_request_log_info(request, subsystem, "Password mismatch");
return;
}
str = t_str_new(128);
get_log_prefix(str, request, subsystem);
str_append(str, "Password mismatch ");
if (strcmp(log_type, "plain") == 0) {
str_printfa(str, "(given password: %s)",
request->mech_password);
} else if (strcmp(log_type, "sha1") == 0) {
unsigned char sha1[SHA1_RESULTLEN];
sha1_get_digest(request->mech_password,
strlen(request->mech_password), sha1);
str_printfa(str, "(SHA1 of given password: %s)",
binary_to_hex(sha1, sizeof(sha1)));
} else {
i_unreached();
}
i_info("%s", str_c(str));
}
int auth_request_password_verify(struct auth_request *request,
const char *plain_password,
const char *crypted_password,
const char *scheme, const char *subsystem)
{
const unsigned char *raw_password;
size_t raw_password_size;
int ret;
if (request->skip_password_check) {
/* currently this can happen only with master logins */
i_assert(request->master_user != NULL);
return 1;
}
if (request->passdb->set->deny) {
/* this is a deny database, we don't care about the password */
return 0;
}
if (request->no_password) {
auth_request_log_debug(request, subsystem,
"Allowing any password");
return 1;
}
ret = password_decode(crypted_password, scheme,
&raw_password, &raw_password_size);
if (ret <= 0) {
if (ret < 0) {
auth_request_log_error(request, subsystem,
"Password in passdb is not in expected scheme %s",
scheme);
} else {
auth_request_log_error(request, subsystem,
"Unknown scheme %s", scheme);
}
return -1;
}
/* Use original_username since it may be important for some
password schemes (eg. digest-md5). Otherwise the username is used
only for logging purposes. */
ret = password_verify(plain_password, request->original_username,
scheme, raw_password, raw_password_size);
i_assert(ret >= 0);
if (ret == 0) {
auth_request_log_password_mismatch(request, subsystem);
if (request->set->debug_passwords) T_BEGIN {
log_password_failure(request, plain_password,
crypted_password, scheme,
request->original_username,
subsystem);
} T_END;
}
return ret;
}
static const char *
escape_none(const char *string,
const struct auth_request *request ATTR_UNUSED)
{
return string;
}
const char *
auth_request_str_escape(const char *string,
const struct auth_request *request ATTR_UNUSED)
{
return str_escape(string);
}
const struct var_expand_table *
auth_request_get_var_expand_table(const struct auth_request *auth_request,
auth_request_escape_func_t *escape_func)
{
static struct var_expand_table static_tab[] = {
{ 'u', NULL, "user" },
{ 'n', NULL, "username" },
{ 'd', NULL, "domain" },
{ 's', NULL, "service" },
{ 'h', NULL, "home" },
{ 'l', NULL, "lip" },
{ 'r', NULL, "rip" },
{ 'p', NULL, "pid" },
{ 'w', NULL, "password" },
{ '!', NULL, NULL },
{ 'm', NULL, "mech" },
{ 'c', NULL, "secured" },
{ 'a', NULL, "lport" },
{ 'b', NULL, "rport" },
{ 'k', NULL, "cert" },
{ '\0', NULL, "login_user" },
{ '\0', NULL, "login_username" },
{ '\0', NULL, "login_domain" },
{ '\0', NULL, NULL }
};
struct var_expand_table *tab;
if (escape_func == NULL)
escape_func = escape_none;
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
tab[0].value = escape_func(auth_request->user, auth_request);
tab[1].value = escape_func(t_strcut(auth_request->user, '@'),
auth_request);
tab[2].value = strchr(auth_request->user, '@');
if (tab[2].value != NULL)
tab[2].value = escape_func(tab[2].value+1, auth_request);
tab[3].value = auth_request->service;
/* tab[4] = we have no home dir */
if (auth_request->local_ip.family != 0)
tab[5].value = net_ip2addr(&auth_request->local_ip);
if (auth_request->remote_ip.family != 0)
tab[6].value = net_ip2addr(&auth_request->remote_ip);
tab[7].value = dec2str(auth_request->client_pid);
if (auth_request->mech_password != NULL) {
tab[8].value = escape_func(auth_request->mech_password,
auth_request);
}
if (auth_request->userdb_lookup) {
tab[9].value = auth_request->userdb == NULL ? "" :
dec2str(auth_request->userdb->userdb->id);
} else {
tab[9].value = auth_request->passdb == NULL ? "" :
dec2str(auth_request->passdb->passdb->id);
}
tab[10].value = auth_request->mech_name == NULL ? "" :
auth_request->mech_name;
tab[11].value = auth_request->secured ? "secured" : "";
tab[12].value = dec2str(auth_request->local_port);
tab[13].value = dec2str(auth_request->remote_port);
tab[14].value = auth_request->valid_client_cert ? "valid" : "";
if (auth_request->requested_login_user != NULL) {
const char *login_user = auth_request->requested_login_user;
tab[15].value = escape_func(login_user, auth_request);
tab[16].value = escape_func(t_strcut(login_user, '@'),
auth_request);
tab[17].value = strchr(login_user, '@');
if (tab[17].value != NULL) {
tab[17].value = escape_func(tab[17].value+1,
auth_request);
}
}
return tab;
}
static void get_log_prefix(string_t *str, struct auth_request *auth_request,
const char *subsystem)
{
#define MAX_LOG_USERNAME_LEN 64
const char *ip;
str_append(str, subsystem);
str_append_c(str, '(');
if (auth_request->user == NULL)
str_append(str, "?");
else {
str_sanitize_append(str, auth_request->user,
MAX_LOG_USERNAME_LEN);
}
ip = net_ip2addr(&auth_request->remote_ip);
if (ip != NULL) {
str_append_c(str, ',');
str_append(str, ip);
}
if (auth_request->requested_login_user != NULL)
str_append(str, ",master");
str_append(str, "): ");
}
static const char * ATTR_FORMAT(3, 0)
get_log_str(struct auth_request *auth_request, const char *subsystem,
const char *format, va_list va)
{
string_t *str;
str = t_str_new(128);
get_log_prefix(str, auth_request, subsystem);
str_vprintfa(str, format, va);
return str_c(str);
}
void auth_request_log_debug(struct auth_request *auth_request,
const char *subsystem,
const char *format, ...)
{
va_list va;
if (!auth_request->set->debug)
return;
va_start(va, format);
T_BEGIN {
i_debug("%s", get_log_str(auth_request, subsystem, format, va));
} T_END;
va_end(va);
}
void auth_request_log_info(struct auth_request *auth_request,
const char *subsystem,
const char *format, ...)
{
va_list va;
if (!auth_request->set->verbose)
return;
va_start(va, format);
T_BEGIN {
i_info("%s", get_log_str(auth_request, subsystem, format, va));
} T_END;
va_end(va);
}
void auth_request_log_warning(struct auth_request *auth_request,
const char *subsystem,
const char *format, ...)
{
va_list va;
va_start(va, format);
T_BEGIN {
i_warning("%s", get_log_str(auth_request, subsystem, format, va));
} T_END;
va_end(va);
}
void auth_request_log_error(struct auth_request *auth_request,
const char *subsystem,
const char *format, ...)
{
va_list va;
va_start(va, format);
T_BEGIN {
i_error("%s", get_log_str(auth_request, subsystem, format, va));
} T_END;
va_end(va);
}
void auth_request_refresh_last_access(struct auth_request *request)
{
request->last_access = ioloop_time;
if (request->to_abort != NULL)
timeout_reset(request->to_abort);
}