auth-request-handler.c revision 814bf67459ad405a157af0b8940602024d7fadfe
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek/* Copyright (c) 2005-2015 Dovecot authors, see the included COPYING file */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "auth-common.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "ioloop.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "array.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "aqueue.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "base64.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "hash.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "net.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "str.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "strescape.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "str-sanitize.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "master-interface.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "auth-penalty.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "auth-request.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "auth-token.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "auth-master-connection.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#include "auth-request-handler.h"
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#define AUTH_FAILURE_DELAY_CHECK_MSECS 500
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstruct auth_request_handler {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek int refcount;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek pool_t pool;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek HASH_TABLE(void *, struct auth_request *) requests;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek unsigned int connect_uid, client_pid;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_callback_t *callback;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek void *context;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_callback_t *master_callback;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek unsigned int destroyed:1;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek unsigned int token_auth:1;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek};
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic ARRAY(struct auth_request *) auth_failures_arr;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic struct aqueue *auth_failures;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic struct timeout *to_auth_failures;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void auth_failure_timeout(void *context) ATTR_NULL(1);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek#undef auth_request_handler_create
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstruct auth_request_handler *
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_request_handler_create(bool token_auth, auth_request_callback_t *callback,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek void *context, auth_request_callback_t *master_callback)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek pool_t pool;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek pool = pool_alloconly_create("auth request handler", 4096);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler = p_new(pool, struct auth_request_handler, 1);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->refcount = 1;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->pool = pool;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek hash_table_create_direct(&handler->requests, pool, 0);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback = callback;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->context = context;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->master_callback = master_callback;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->token_auth = token_auth;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekunsigned int
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_request_handler_get_request_count(struct auth_request_handler *handler)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return hash_table_count(handler->requests);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekvoid auth_request_handler_abort_requests(struct auth_request_handler *handler)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct hash_iterate_context *iter;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek void *key;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request *auth_request;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek iter = hash_table_iterate_init(handler->requests);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek while (hash_table_iterate(iter, handler->requests, &key, &auth_request)) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek switch (auth_request->state) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_REQUEST_STATE_NEW:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_REQUEST_STATE_MECH_CONTINUE:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_REQUEST_STATE_FINISHED:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_unref(&auth_request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek hash_table_remove(handler->requests, key);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_REQUEST_STATE_PASSDB:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_REQUEST_STATE_USERDB:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* can't abort a pending passdb/userdb lookup */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_REQUEST_STATE_MAX:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek i_unreached();
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek hash_table_iterate_deinit(&iter);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekvoid auth_request_handler_unref(struct auth_request_handler **_handler)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler = *_handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek *_handler = NULL;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek i_assert(handler->refcount > 0);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (--handler->refcount > 0)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek i_assert(hash_table_count(handler->requests) == 0);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* notify parent that we're done with all requests */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback(NULL, handler->context);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek hash_table_destroy(&handler->requests);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek pool_unref(&handler->pool);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekvoid auth_request_handler_destroy(struct auth_request_handler **_handler)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler = *_handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek *_handler = NULL;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek i_assert(!handler->destroyed);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->destroyed = TRUE;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_unref(&handler);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekvoid auth_request_handler_set(struct auth_request_handler *handler,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek unsigned int connect_uid,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek unsigned int client_pid)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
234958be042980242fff6da936af674da877c5efSimo Sorce handler->connect_uid = connect_uid;
234958be042980242fff6da936af674da877c5efSimo Sorce handler->client_pid = client_pid;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void auth_request_handler_remove(struct auth_request_handler *handler,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request *request)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
234958be042980242fff6da936af674da877c5efSimo Sorce i_assert(request->handler == handler);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->removed_from_handler) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* already removed it */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->removed_from_handler = TRUE;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* if db lookup is stuck, this call doesn't actually free the auth
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request, so make sure we don't get back here. */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek timeout_remove(&request->to_abort);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek hash_table_remove(handler->requests, POINTER_CAST(request->id));
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_unref(&request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_str_add_keyvalue(string_t *dest, const char *key, const char *value)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append_c(dest, '\t');
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append(dest, key);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append_c(dest, '=');
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append_tabescaped(dest, value);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholastaauth_str_append_extra_fields(struct auth_request *request, string_t *dest)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (!auth_fields_is_empty(request->extra_fields)) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append_c(dest, '\t');
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_append(request->extra_fields, dest,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek AUTH_FIELD_FLAG_HIDDEN, 0);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->original_username != NULL &&
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek null_strcmp(request->original_username, request->user) != 0 &&
965428847850f1b154130e249f2d942c6065e88dSimo Sorce !auth_fields_exists(request->extra_fields, "original_user")) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_add_keyvalue(dest, "original_user",
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->original_username);
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->master_user != NULL &&
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek !auth_fields_exists(request->extra_fields, "auth_user"))
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_add_keyvalue(dest, "auth_user", request->master_user);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (!request->auth_only &&
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_exists(request->extra_fields, "proxy")) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* we're proxying */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (!auth_fields_exists(request->extra_fields, "pass") &&
965428847850f1b154130e249f2d942c6065e88dSimo Sorce request->mech_password != NULL) {
965428847850f1b154130e249f2d942c6065e88dSimo Sorce /* send back the password that was sent by user
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek (not the password in passdb). */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_add_keyvalue(dest, "pass",
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->mech_password);
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->master_user != NULL &&
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek !auth_fields_exists(request->extra_fields, "master")) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* the master username needs to be forwarded */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_add_keyvalue(dest, "master",
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta request->master_user);
965428847850f1b154130e249f2d942c6065e88dSimo Sorce }
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta }
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_request_handle_failure(struct auth_request *request, const char *reply)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler = request->handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->in_delayed_failure_queue) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* we came here from flush_failures() */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback(reply, handler->context);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* remove the request from requests-list */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_ref(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_remove(handler, request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (auth_fields_exists(request->extra_fields, "nodelay")) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* passdb specifically requested not to delay the reply. */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback(reply, handler->context);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_unref(&request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* failure. don't announce it immediately to avoid
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek a) timing attacks, b) flooding */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->in_delayed_failure_queue = TRUE;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->refcount++;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (auth_penalty != NULL) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_penalty_update(auth_penalty, request,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->last_penalty + 1);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_refresh_last_access(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek aqueue_append(auth_failures, &request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (to_auth_failures == NULL) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek to_auth_failures =
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek timeout_add_short(AUTH_FAILURE_DELAY_CHECK_MSECS,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_failure_timeout, (void *)NULL);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_request_handler_reply_success_finish(struct auth_request *request)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler = request->handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek string_t *str = t_str_new(128);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->last_penalty != 0 && auth_penalty != NULL) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* reset penalty */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_penalty_update(auth_penalty, request, 0);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* sanitize these fields, since the login code currently assumes they
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek are exactly in this format. */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_booleanize(request->extra_fields, "nologin");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_booleanize(request->extra_fields, "proxy");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_printfa(str, "OK\t%u\tuser=", request->id);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append_tabescaped(str, request->user);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_append_extra_fields(request, str);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (handler->master_callback == NULL ||
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_exists(request->extra_fields, "nologin") ||
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_exists(request->extra_fields, "proxy")) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* this request doesn't have to wait for master
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek process to pick it up. delete it */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_remove(handler, request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback(str_c(str), handler->context);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_request_handler_reply_failure_finish(struct auth_request *request)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek string_t *str = t_str_new(128);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_remove(request->extra_fields, "nologin");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_printfa(str, "FAIL\t%u", request->id);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->user != NULL)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_add_keyvalue(str, "user", request->user);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek else if (request->original_username != NULL) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_add_keyvalue(str, "user",
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->original_username);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (request->internal_failure)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append(str, "\ttemp");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek else if (request->master_user != NULL) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* authentication succeeded, but we can't log in
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek as the wanted user */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append(str, "\tauthz");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (auth_fields_exists(request->extra_fields, "nodelay")) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* this is normally a hidden field, need to add it explicitly */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append(str, "\tnodelay");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_str_append_extra_fields(request, str);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek switch (request->passdb_result) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_INTERNAL_FAILURE:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_SCHEME_NOT_AVAILABLE:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_USER_UNKNOWN:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_PASSWORD_MISMATCH:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_OK:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_USER_DISABLED:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append(str, "\tuser_disabled");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case PASSDB_RESULT_PASS_EXPIRED:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append(str, "\tpass_expired");
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handle_failure(request, str_c(str));
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekauth_request_handler_proxy_callback(bool success, struct auth_request *request)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler = request->handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (success)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_reply_success_finish(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek else
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_reply_failure_finish(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_unref(&handler);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekvoid auth_request_handler_reply(struct auth_request *request,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek enum auth_client_result result,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek const void *auth_reply, size_t reply_size)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request_handler *handler = request->handler;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek string_t *str;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek int ret;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (handler->destroyed) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* the client connection was already closed. we can't do
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek anything but abort this request */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->internal_failure = TRUE;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek result = AUTH_CLIENT_RESULT_FAILURE;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* make sure this request is set to finished state
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek (it's not with result=continue) */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek switch (result) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_CLIENT_RESULT_CONTINUE:
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str = t_str_new(16 + MAX_BASE64_ENCODED_SIZE(reply_size));
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_printfa(str, "CONT\t%u\t", request->id);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek base64_encode(auth_reply, reply_size, str);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->accept_cont_input = TRUE;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback(str_c(str), handler->context);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_CLIENT_RESULT_SUCCESS:
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta if (reply_size > 0) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str = t_str_new(MAX_BASE64_ENCODED_SIZE(reply_size));
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek base64_encode(auth_reply, reply_size, str);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_fields_add(request->extra_fields, "resp",
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_c(str), 0);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek ret = auth_request_proxy_finish(request,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_proxy_callback);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek if (ret < 0)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_reply_failure_finish(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek else if (ret > 0)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_reply_success_finish(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek else
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek return;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek case AUTH_CLIENT_RESULT_FAILURE:
2d6836a90bd326391782a5753f70e8ba666b5defJan Cholasta auth_request_proxy_finish_failure(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_reply_failure_finish(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek break;
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek /* NOTE: request may be destroyed now */
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_unref(&handler);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekvoid auth_request_handler_reply_continue(struct auth_request *request,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek const void *reply, size_t reply_size)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_reply(request, AUTH_CLIENT_RESULT_CONTINUE,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek reply, reply_size);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void auth_request_handler_auth_fail(struct auth_request_handler *handler,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek struct auth_request *request,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek const char *reason)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek string_t *str = t_str_new(128);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", reason);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_printfa(str, "FAIL\t%u\treason=", request->id);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek str_append_tabescaped(str, reason);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek handler->callback(str_c(str), handler->context);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_remove(handler, request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
9e2c64c6d4f5560e27207193efea6536a566865eMichal Zidekstatic void auth_request_timeout(struct auth_request *request)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
dfe84158c49e44f2207b94d25e61ab4f3fe38366Michal Zidek unsigned int secs = (unsigned int)(time(NULL) - request->last_access);
dfe84158c49e44f2207b94d25e61ab4f3fe38366Michal Zidek
dfe84158c49e44f2207b94d25e61ab4f3fe38366Michal Zidek if (request->state != AUTH_REQUEST_STATE_MECH_CONTINUE) {
dfe84158c49e44f2207b94d25e61ab4f3fe38366Michal Zidek /* client's fault */
dfe84158c49e44f2207b94d25e61ab4f3fe38366Michal Zidek auth_request_log_error(request, AUTH_SUBSYS_MECH,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek "Request %u.%u timed out after %u secs, state=%d",
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek request->handler->client_pid, request->id,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek secs, request->state);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek } else if (request->set->verbose) {
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_log_info(request, AUTH_SUBSYS_MECH,
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek "Request timed out waiting for client to continue authentication "
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek "(%u secs)", secs);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek }
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_handler_remove(request->handler, request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidekstatic void auth_request_penalty_finish(struct auth_request *request)
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek{
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek timeout_remove(&request->to_penalty);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek auth_request_initial(request);
25f8fac2489fd209d603acb2b494f7c72968e9bbMichal Zidek}
static void
auth_penalty_callback(unsigned int penalty, struct auth_request *request)
{
unsigned int secs;
request->last_penalty = penalty;
if (penalty == 0)
auth_request_initial(request);
else {
secs = auth_penalty_to_secs(penalty);
request->to_penalty = timeout_add(secs * 1000,
auth_request_penalty_finish,
request);
}
}
bool auth_request_handler_auth_begin(struct auth_request_handler *handler,
const char *args)
{
const struct mech_module *mech;
struct auth_request *request;
const char *const *list, *name, *arg, *initial_resp;
void *initial_resp_data;
unsigned int id;
buffer_t *buf;
i_assert(!handler->destroyed);
/* <id> <mechanism> [...] */
list = t_strsplit_tab(args);
if (list[0] == NULL || list[1] == NULL ||
str_to_uint(list[0], &id) < 0) {
i_error("BUG: Authentication client %u "
"sent broken AUTH request", handler->client_pid);
return FALSE;
}
if (handler->token_auth) {
mech = &mech_dovecot_token;
if (strcmp(list[1], mech->mech_name) != 0) {
/* unsupported mechanism */
i_error("BUG: Authentication client %u requested invalid "
"authentication mechanism %s (DOVECOT-TOKEN required)",
handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN));
return FALSE;
}
} else {
struct auth *auth_default = auth_default_service();
mech = mech_register_find(auth_default->reg, list[1]);
if (mech == NULL) {
/* unsupported mechanism */
i_error("BUG: Authentication client %u requested unsupported "
"authentication mechanism %s", handler->client_pid,
str_sanitize(list[1], MAX_MECH_NAME_LEN));
return FALSE;
}
}
request = auth_request_new(mech);
request->handler = handler;
request->connect_uid = handler->connect_uid;
request->client_pid = handler->client_pid;
request->id = id;
request->auth_only = handler->master_callback == NULL;
/* parse optional parameters */
initial_resp = NULL;
for (list += 2; *list != NULL; list++) {
arg = strchr(*list, '=');
if (arg == NULL) {
name = *list;
arg = "";
} else {
name = t_strdup_until(*list, arg);
arg++;
}
if (auth_request_import_auth(request, name, arg))
;
else if (strcmp(name, "resp") == 0) {
initial_resp = arg;
/* this must be the last parameter */
list++;
break;
}
}
if (*list != NULL) {
i_error("BUG: Authentication client %u "
"sent AUTH parameters after 'resp'",
handler->client_pid);
auth_request_unref(&request);
return FALSE;
}
if (request->service == NULL) {
i_error("BUG: Authentication client %u "
"didn't specify service in request",
handler->client_pid);
auth_request_unref(&request);
return FALSE;
}
if (hash_table_lookup(handler->requests, POINTER_CAST(id)) != NULL) {
i_error("BUG: Authentication client %u "
"sent a duplicate ID %u", handler->client_pid, id);
auth_request_unref(&request);
return FALSE;
}
auth_request_init(request);
request->to_abort = timeout_add(MASTER_AUTH_SERVER_TIMEOUT_SECS * 1000,
auth_request_timeout, request);
hash_table_insert(handler->requests, POINTER_CAST(id), request);
if (request->set->ssl_require_client_cert &&
!request->valid_client_cert) {
/* we fail without valid certificate */
auth_request_handler_auth_fail(handler, request,
"Client didn't present valid SSL certificate");
return TRUE;
}
/* Empty initial response is a "=" base64 string. Completely empty
string shouldn't really be sent, but at least Exim does it,
so just allow it for backwards compatibility.. */
if (initial_resp != NULL && *initial_resp != '\0') {
size_t len = strlen(initial_resp);
buf = buffer_create_dynamic(pool_datastack_create(),
MAX_BASE64_DECODED_SIZE(len));
if (base64_decode(initial_resp, len, NULL, buf) < 0) {
auth_request_handler_auth_fail(handler, request,
"Invalid base64 data in initial response");
return TRUE;
}
initial_resp_data =
p_malloc(request->pool, I_MAX(buf->used, 1));
memcpy(initial_resp_data, buf->data, buf->used);
request->initial_response = initial_resp_data;
request->initial_response_len = buf->used;
}
/* handler is referenced until auth_request_handler_reply()
is called. */
handler->refcount++;
/* before we start authenticating, see if we need to wait first */
auth_penalty_lookup(auth_penalty, request, auth_penalty_callback);
return TRUE;
}
bool auth_request_handler_auth_continue(struct auth_request_handler *handler,
const char *args)
{
struct auth_request *request;
const char *data;
size_t data_len;
buffer_t *buf;
unsigned int id;
data = strchr(args, '\t');
if (data == NULL || str_to_uint(t_strdup_until(args, data), &id) < 0) {
i_error("BUG: Authentication client sent broken CONT request");
return FALSE;
}
data++;
request = hash_table_lookup(handler->requests, POINTER_CAST(id));
if (request == NULL) {
const char *reply = t_strdup_printf(
"FAIL\t%u\treason=Authentication request timed out", id);
handler->callback(reply, handler->context);
return TRUE;
}
/* accept input only once after mechanism has sent a CONT reply */
if (!request->accept_cont_input) {
auth_request_handler_auth_fail(handler, request,
"Unexpected continuation");
return TRUE;
}
request->accept_cont_input = FALSE;
data_len = strlen(data);
buf = buffer_create_dynamic(pool_datastack_create(),
MAX_BASE64_DECODED_SIZE(data_len));
if (base64_decode(data, data_len, NULL, buf) < 0) {
auth_request_handler_auth_fail(handler, request,
"Invalid base64 data in continued response");
return TRUE;
}
/* handler is referenced until auth_request_handler_reply()
is called. */
handler->refcount++;
auth_request_continue(request, buf->data, buf->used);
return TRUE;
}
static void auth_str_append_userdb_extra_fields(struct auth_request *request,
string_t *dest)
{
str_append_c(dest, '\t');
auth_fields_append(request->userdb_reply, dest,
AUTH_FIELD_FLAG_HIDDEN, 0);
if (request->master_user != NULL &&
!auth_fields_exists(request->userdb_reply, "master_user")) {
auth_str_add_keyvalue(dest, "master_user",
request->master_user);
}
if (*request->set->anonymous_username != '\0' &&
strcmp(request->user, request->set->anonymous_username) == 0) {
/* this is an anonymous login, either via ANONYMOUS
SASL mechanism or simply logging in as the anonymous
user via another mechanism */
str_append(dest, "\tanonymous");
}
/* generate auth_token when master service provided session_pid */
if (request->request_auth_token &&
request->session_pid != (pid_t)-1) {
const char *auth_token =
auth_token_get(request->service,
dec2str(request->session_pid),
request->user,
request->session_id);
auth_str_add_keyvalue(dest, "auth_token", auth_token);
}
if (request->master_user != NULL) {
auth_str_add_keyvalue(dest, "auth_user", request->master_user);
} else if (request->original_username != NULL &&
strcmp(request->original_username, request->user) != 0) {
auth_str_add_keyvalue(dest, "auth_user",
request->original_username);
}
}
static void userdb_callback(enum userdb_result result,
struct auth_request *request)
{
struct auth_request_handler *handler = request->handler;
string_t *str;
const char *value;
i_assert(request->state == AUTH_REQUEST_STATE_USERDB);
auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
if (request->userdb_lookup_tempfailed)
result = USERDB_RESULT_INTERNAL_FAILURE;
str = t_str_new(128);
switch (result) {
case USERDB_RESULT_INTERNAL_FAILURE:
str_printfa(str, "FAIL\t%u", request->id);
if (request->userdb_lookup_tempfailed) {
value = auth_fields_find(request->userdb_reply, "reason");
if (value != NULL)
auth_str_add_keyvalue(str, "reason", value);
}
break;
case USERDB_RESULT_USER_UNKNOWN:
str_printfa(str, "NOTFOUND\t%u", request->id);
break;
case USERDB_RESULT_OK:
str_printfa(str, "USER\t%u\t", request->id);
str_append_tabescaped(str, request->user);
auth_str_append_userdb_extra_fields(request, str);
break;
}
handler->master_callback(str_c(str), request->master);
auth_master_connection_unref(&request->master);
auth_request_unref(&request);
auth_request_handler_unref(&handler);
}
static bool
auth_master_request_failed(struct auth_request_handler *handler,
struct auth_master_connection *master,
unsigned int id)
{
if (handler->master_callback == NULL)
return FALSE;
handler->master_callback(t_strdup_printf("FAIL\t%u", id), master);
return TRUE;
}
bool auth_request_handler_master_request(struct auth_request_handler *handler,
struct auth_master_connection *master,
unsigned int id, unsigned int client_id,
const char *const *params)
{
struct auth_request *request;
struct net_unix_cred cred;
request = hash_table_lookup(handler->requests, POINTER_CAST(client_id));
if (request == NULL) {
i_error("Master request %u.%u not found",
handler->client_pid, client_id);
return auth_master_request_failed(handler, master, id);
}
auth_request_ref(request);
auth_request_handler_remove(handler, request);
for (; *params != NULL; params++) {
const char *name, *param = strchr(*params, '=');
if (param == NULL) {
name = *params;
param = "";
} else {
name = t_strdup_until(*params, param);
param++;
}
(void)auth_request_import_master(request, name, param);
}
/* verify session pid if specified and possible */
if (request->session_pid != (pid_t)-1 &&
net_getunixcred(master->fd, &cred) == 0 &&
cred.pid != (pid_t)-1 && request->session_pid != cred.pid) {
i_error("Session pid %ld provided by master for request %u.%u "
"did not match peer credentials (pid=%ld, uid=%ld)",
(long)request->session_pid,
handler->client_pid, client_id,
(long)cred.pid, (long)cred.uid);
return auth_master_request_failed(handler, master, id);
}
if (request->state != AUTH_REQUEST_STATE_FINISHED ||
!request->successful) {
i_error("Master requested unfinished authentication request "
"%u.%u", handler->client_pid, client_id);
handler->master_callback(t_strdup_printf("FAIL\t%u", id),
master);
auth_request_unref(&request);
} else {
/* the request isn't being referenced anywhere anymore,
so we can do a bit of kludging.. replace the request's
old client_id with master's id. */
auth_request_set_state(request, AUTH_REQUEST_STATE_USERDB);
request->id = id;
request->master = master;
/* master and handler are referenced until userdb_callback i
s called. */
auth_master_connection_ref(master);
handler->refcount++;
auth_request_lookup_user(request, userdb_callback);
}
return TRUE;
}
void auth_request_handler_cancel_request(struct auth_request_handler *handler,
unsigned int client_id)
{
struct auth_request *request;
request = hash_table_lookup(handler->requests, POINTER_CAST(client_id));
if (request != NULL)
auth_request_handler_remove(handler, request);
}
void auth_request_handler_flush_failures(bool flush_all)
{
struct auth_request **auth_requests, *auth_request;
unsigned int i, count;
time_t diff;
count = aqueue_count(auth_failures);
if (count == 0) {
if (to_auth_failures != NULL)
timeout_remove(&to_auth_failures);
return;
}
auth_requests = array_idx_modifiable(&auth_failures_arr, 0);
for (i = 0; i < count; i++) {
auth_request = auth_requests[aqueue_idx(auth_failures, 0)];
/* FIXME: assumess that failure_delay is always the same. */
diff = ioloop_time - auth_request->last_access;
if (diff < (time_t)auth_request->set->failure_delay &&
!flush_all)
break;
aqueue_delete_tail(auth_failures);
i_assert(auth_request->state == AUTH_REQUEST_STATE_FINISHED);
auth_request_handler_reply(auth_request,
AUTH_CLIENT_RESULT_FAILURE,
&uchar_nul, 0);
auth_request_unref(&auth_request);
}
}
static void auth_failure_timeout(void *context ATTR_UNUSED)
{
auth_request_handler_flush_failures(FALSE);
}
void auth_request_handler_init(void)
{
i_array_init(&auth_failures_arr, 128);
auth_failures = aqueue_init(&auth_failures_arr.arr);
}
void auth_request_handler_deinit(void)
{
auth_request_handler_flush_failures(TRUE);
array_free(&auth_failures_arr);
aqueue_deinit(&auth_failures);
if (to_auth_failures != NULL)
timeout_remove(&to_auth_failures);
}