client-common.c revision 0f5dc4da3982053036be65190e44bf28a67b1ca2
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "login-common.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "array.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "hostpid.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "llist.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "istream.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "ostream.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "iostream-rawlog.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "process-title.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "buffer.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "str.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "base64.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "str-sanitize.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "safe-memset.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "var-expand.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "master-interface.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "master-service.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "master-service-ssl-settings.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "master-auth.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "auth-client.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "dsasl-client.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "login-proxy.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "ssl-proxy.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "client-common.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstruct client *clients = NULL;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic struct client *last_client = NULL;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainenstatic unsigned int clients_count = 0;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic void empty_login_client_allocated_hook(struct client *client ATTR_UNUSED)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic login_client_allocated_func_t *hook_client_allocated =
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen empty_login_client_allocated_hook;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenlogin_client_allocated_func_t *
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenlogin_client_allocated_hook_set(login_client_allocated_func_t *new_hook)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen login_client_allocated_func_t *old_hook = hook_client_allocated;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen hook_client_allocated = new_hook;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return old_hook;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char *user_reason, *destroy_reason;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int secs;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->master_tag != 0) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen secs = ioloop_time - client->auth_finished;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen user_reason = "Timeout while finishing login.";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen destroy_reason = t_strdup_printf(
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "Timeout while finishing login (waited %u secs)", secs);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_log_err(client, destroy_reason);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen } else if (client->auth_request != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen user_reason =
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "Disconnected for inactivity during authentication.";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen destroy_reason =
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "Disconnected: Inactivity during authentication";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen } else if (client->login_proxy != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen secs = ioloop_time - client->created;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen user_reason = "Timeout while finishing login.";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen destroy_reason = t_strdup_printf(
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "proxy: Logging in to %s:%u timed out "
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "(state=%u, duration=%us)",
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen login_proxy_get_host(client->login_proxy),
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen login_proxy_get_port(client->login_proxy),
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->proxy_state, secs);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_log_err(client, destroy_reason);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen } else {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen user_reason = "Disconnected for inactivity.";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen destroy_reason = "Disconnected: Inactivity";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_destroy(client, destroy_reason);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic void client_open_streams(struct client *client)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->input = i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->output = o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (login_rawlog_dir != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (iostream_rawlog_create(login_rawlog_dir, &client->input,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen &client->output) < 0)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen login_rawlog_dir = NULL;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic bool client_is_trusted(struct client *client)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char *const *net;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct ip_addr net_ip;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int bits;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->set->login_trusted_networks == NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return FALSE;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen for (; *net != NULL; net++) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (net_parse_range(*net, &net_ip, &bits) < 0) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_error("login_trusted_networks: "
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "Invalid network '%s'", *net);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen break;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (net_is_in_network(&client->ip, &net_ip, bits))
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return TRUE;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return FALSE;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstruct client *
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenclient_create(int fd, bool ssl, pool_t pool,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const struct master_service_connection *conn,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const struct login_settings *set,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const struct master_service_ssl_settings *ssl_set,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen void **other_sets)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct client *client;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(fd != -1);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client = login_binary->client_vfuncs->alloc(pool);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->v = *login_binary->client_vfuncs;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->v.auth_send_challenge == NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->v.auth_parse_response == NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->v.auth_parse_response = client_auth_parse_response;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->created = ioloop_time;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->refcount = 1;
c971768955f826fb965d8ffbb13dac93c9bbead8Phil Carmody
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->pool = pool;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->set = set;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->ssl_set = ssl_set;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen p_array_init(&client->module_contexts, client->pool, 5);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->fd = fd;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->tls = ssl;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->local_ip = conn->local_ip;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->local_port = conn->local_port;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->ip = conn->remote_ip;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->remote_port = conn->remote_port;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->real_local_ip = conn->real_local_ip;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->real_local_port = conn->real_local_port;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->real_remote_ip = conn->real_remote_ip;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen client->real_remote_port = conn->real_remote_port;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen client->listener_name = p_strdup(client->pool, conn->name);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen client->trusted = client_is_trusted(client);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen client->secured = ssl || client->trusted ||
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen net_ip_compare(&conn->real_remote_ip, &conn->real_local_ip);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen client->proxy_ttl = LOGIN_PROXY_TTL;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen if (last_client == NULL)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen last_client = client;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen DLLIST_PREPEND(&clients, client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen clients_count++;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->to_disconnect =
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_idle_disconnect_timeout, client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_open_streams(client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen hook_client_allocated(client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->v.create(client, other_sets);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (auth_client_is_connected(auth_client))
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_notify_auth_ready(client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen else
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_set_auth_waiting(client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen login_refresh_proctitle();
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return client;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenvoid client_destroy(struct client *client, const char *reason)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->destroyed)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->destroyed = TRUE;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (!client->login_success && reason != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen reason = t_strconcat(reason, " ",
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_get_extra_disconnect_reason(client), NULL);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (reason != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client_log(client, reason);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (last_client == client)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen last_client = client->prev;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen DLLIST_REMOVE(&clients, client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->output != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen o_stream_uncork(client->output);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (!client->login_success && client->ssl_proxy != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen ssl_proxy_destroy(client->ssl_proxy);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->input != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_stream_close(client->input);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->output != NULL)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen o_stream_close(client->output);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->master_tag != 0) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(client->auth_request == NULL);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(client->authenticating);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(client->refcount > 1);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->authenticating = FALSE;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen master_auth_request_abort(master_auth, client->master_tag);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->refcount--;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen } else if (client->auth_request != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(client->authenticating);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen sasl_server_auth_abort(client);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen } else {
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen i_assert(!client->authenticating);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->io != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen io_remove(&client->io);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->to_disconnect != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen timeout_remove(&client->to_disconnect);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen if (client->to_auth_waiting != NULL)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen timeout_remove(&client->to_auth_waiting);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen if (client->auth_response != NULL)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen str_free(&client->auth_response);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen if (client->fd != -1) {
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen net_disconnect(client->fd);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen client->fd = -1;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->proxy_password != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen safe_memset(client->proxy_password, 0,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen strlen(client->proxy_password));
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_free_and_null(client->proxy_password);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->proxy_sasl_client != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen dsasl_client_free(&client->proxy_sasl_client);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (client->login_proxy != NULL)
login_proxy_free(&client->login_proxy);
if (client->v.destroy != NULL)
client->v.destroy(client);
if (client_unref(&client) && initial_service_count == 1) {
/* as soon as this connection is done with proxying
(or whatever), the process will die. there's no need for
authentication anymore, so close the connection.
do this only with initial service_count=1, in case there
are other clients with pending authentications */
auth_client_disconnect(auth_client, "unnecessary connection");
}
login_client_destroyed();
login_refresh_proctitle();
}
void client_destroy_success(struct client *client, const char *reason)
{
client->login_success = TRUE;
client_destroy(client, reason);
}
void client_destroy_internal_failure(struct client *client)
{
client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR,
"Internal login failure. "
"Refer to server log for more information.");
client_destroy(client, t_strdup_printf(
"Internal login failure (pid=%s id=%u)",
my_pid, client->master_auth_id));
}
void client_ref(struct client *client)
{
client->refcount++;
}
bool client_unref(struct client **_client)
{
struct client *client = *_client;
i_assert(client->refcount > 0);
if (--client->refcount > 0)
return TRUE;
*_client = NULL;
i_assert(client->destroyed);
i_assert(client->login_proxy == NULL);
if (client->ssl_proxy != NULL)
ssl_proxy_free(&client->ssl_proxy);
if (client->input != NULL)
i_stream_unref(&client->input);
if (client->output != NULL)
o_stream_unref(&client->output);
i_free(client->proxy_user);
i_free(client->proxy_master_user);
i_free(client->virtual_user);
i_free(client->virtual_user_orig);
i_free(client->virtual_auth_user);
i_free(client->auth_mech_name);
i_free(client->master_data_prefix);
pool_unref(&client->pool);
i_assert(clients_count > 0);
clients_count--;
master_service_client_connection_destroyed(master_service);
login_refresh_proctitle();
return FALSE;
}
void client_destroy_oldest(void)
{
struct client *client;
if (last_client == NULL) {
/* we have no clients */
return;
}
/* destroy the last client that hasn't successfully authenticated yet.
this is usually the last client, but don't kill it if it's just
waiting for master to finish its job. */
for (client = last_client; client != NULL; client = client->prev) {
if (client->master_tag == 0)
break;
}
if (client == NULL)
client = last_client;
client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT,
"Connection queue full");
client_destroy(client, "Disconnected: Connection queue full");
}
void clients_destroy_all_reason(const char *reason)
{
struct client *client, *next;
for (client = clients; client != NULL; client = next) {
next = client->next;
client_notify_disconnect(client,
CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, reason);
client_destroy(client, reason);
}
}
void clients_destroy_all(void)
{
clients_destroy_all_reason("Disconnected: Shutting down");
}
static void client_start_tls(struct client *client)
{
int fd_ssl;
client_ref(client);
if (!client_unref(&client) || client->destroyed)
return;
fd_ssl = ssl_proxy_alloc(client->fd, &client->ip, client->pool,
client->set, client->ssl_set,
&client->ssl_proxy);
if (fd_ssl == -1) {
client_notify_disconnect(client,
CLIENT_DISCONNECT_INTERNAL_ERROR,
"TLS initialization failed.");
client_destroy(client,
"Disconnected: TLS initialization failed.");
return;
}
ssl_proxy_set_client(client->ssl_proxy, client);
ssl_proxy_start(client->ssl_proxy);
client->starttls = TRUE;
client->tls = TRUE;
client->secured = TRUE;
login_refresh_proctitle();
client->fd = fd_ssl;
client->io = io_add(client->fd, IO_READ, client_input, client);
i_stream_unref(&client->input);
o_stream_unref(&client->output);
client_open_streams(client);
client->v.starttls(client);
}
static int client_output_starttls(struct client *client)
{
int ret;
if ((ret = o_stream_flush(client->output)) < 0) {
client_destroy(client, "Disconnected");
return 1;
}
if (ret > 0) {
o_stream_unset_flush_callback(client->output);
client_start_tls(client);
}
return 1;
}
void client_cmd_starttls(struct client *client)
{
if (client->tls) {
client->v.notify_starttls(client, FALSE, "TLS is already active.");
return;
}
if (!client_is_tls_enabled(client)) {
client->v.notify_starttls(client, FALSE, "TLS support isn't enabled.");
return;
}
/* remove input handler, SSL proxy gives us a new fd. we also have to
remove it in case we have to wait for buffer to be flushed */
if (client->io != NULL)
io_remove(&client->io);
client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now.");
/* uncork the old fd */
o_stream_uncork(client->output);
if (o_stream_flush(client->output) <= 0) {
/* the buffer has to be flushed */
o_stream_set_flush_pending(client->output, TRUE);
o_stream_set_flush_callback(client->output,
client_output_starttls, client);
} else {
client_start_tls(client);
}
}
unsigned int clients_get_count(void)
{
return clients_count;
}
const char *client_get_session_id(struct client *client)
{
buffer_t *buf, *base64_buf;
struct timeval tv;
uint64_t timestamp;
unsigned int i;
if (client->session_id != NULL)
return client->session_id;
buf = buffer_create_dynamic(pool_datastack_create(), 24);
base64_buf = buffer_create_dynamic(pool_datastack_create(), 24*2);
if (gettimeofday(&tv, NULL) < 0)
i_fatal("gettimeofday(): %m");
timestamp = tv.tv_usec + (long long)tv.tv_sec * 1000ULL*1000ULL;
/* add lowest 48 bits of the timestamp. this gives us a bit less than
9 years until it wraps */
for (i = 0; i < 48; i += 8)
buffer_append_c(buf, (timestamp >> i) & 0xff);
buffer_append_c(buf, client->remote_port & 0xff);
buffer_append_c(buf, (client->remote_port >> 8) & 0xff);
if (IPADDR_IS_V6(&client->ip))
buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6));
else
buffer_append(buf, &client->ip.u.ip4, sizeof(client->ip.u.ip4));
base64_encode(buf->data, buf->used, base64_buf);
client->session_id = p_strdup(client->pool, str_c(base64_buf));
return client->session_id;
}
static struct var_expand_table login_var_expand_empty_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" },
{ 'm', NULL, "mech" },
{ 'a', NULL, "lport" },
{ 'b', NULL, "rport" },
{ 'c', NULL, "secured" },
{ 'k', NULL, "ssl_security" },
{ 'e', NULL, "mail_pid" },
{ '\0', NULL, "session" },
{ '\0', NULL, "real_lip" },
{ '\0', NULL, "real_rip" },
{ '\0', NULL, "real_lport" },
{ '\0', NULL, "real_rport" },
{ '\0', NULL, "orig_user" },
{ '\0', NULL, "orig_username" },
{ '\0', NULL, "orig_domain" },
{ '\0', NULL, "auth_user" },
{ '\0', NULL, "auth_username" },
{ '\0', NULL, "auth_domain" },
{ '\0', NULL, "listener" },
{ '\0', NULL, "local_name" },
{ '\0', NULL, NULL }
};
static void
get_var_expand_users(struct var_expand_table *tab, const char *user)
{
unsigned int i;
tab[0].value = user;
tab[1].value = t_strcut(user, '@');
tab[2].value = i_strchr_to_next(user, '@');
for (i = 0; i < 3; i++)
tab[i].value = str_sanitize(tab[i].value, 80);
}
static const struct var_expand_table *
get_var_expand_table(struct client *client)
{
struct var_expand_table *tab;
tab = t_malloc_no0(sizeof(login_var_expand_empty_tab));
memcpy(tab, login_var_expand_empty_tab,
sizeof(login_var_expand_empty_tab));
if (client->virtual_user != NULL)
get_var_expand_users(tab, client->virtual_user);
tab[3].value = login_binary->protocol;
tab[4].value = getenv("HOME");
tab[5].value = net_ip2addr(&client->local_ip);
tab[6].value = net_ip2addr(&client->ip);
tab[7].value = my_pid;
tab[8].value = client->auth_mech_name == NULL ? NULL :
str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
tab[9].value = dec2str(client->local_port);
tab[10].value = dec2str(client->remote_port);
if (!client->tls) {
tab[11].value = client->secured ? "secured" : NULL;
tab[12].value = "";
} else {
const char *ssl_state =
ssl_proxy_is_handshaked(client->ssl_proxy) ?
"TLS" : "TLS handshaking";
const char *ssl_error =
ssl_proxy_get_last_error(client->ssl_proxy);
tab[11].value = ssl_error == NULL ? ssl_state :
t_strdup_printf("%s: %s", ssl_state, ssl_error);
tab[12].value =
ssl_proxy_get_security_string(client->ssl_proxy);
}
tab[13].value = client->mail_pid == 0 ? "" :
dec2str(client->mail_pid);
tab[14].value = client_get_session_id(client);
tab[15].value = net_ip2addr(&client->real_local_ip);
tab[16].value = net_ip2addr(&client->real_remote_ip);
tab[17].value = dec2str(client->real_local_port);
tab[18].value = dec2str(client->real_remote_port);
if (client->virtual_user_orig != NULL)
get_var_expand_users(tab+19, client->virtual_user_orig);
else {
tab[19].value = tab[0].value;
tab[20].value = tab[1].value;
tab[21].value = tab[2].value;
}
if (client->virtual_auth_user != NULL)
get_var_expand_users(tab+22, client->virtual_auth_user);
else {
tab[22].value = tab[19].value;
tab[23].value = tab[20].value;
tab[24].value = tab[21].value;
}
tab[25].value = client->listener_name;
tab[26].value = str_sanitize(client->local_name, 256);
return tab;
}
static bool have_username_key(const char *str)
{
char key;
for (; *str != '\0'; str++) {
if (str[0] == '%' && str[1] != '\0') {
str++;
key = var_get_key(str);
if (key == 'u' || key == 'n')
return TRUE;
}
}
return FALSE;
}
static const char *
client_var_expand_func_passdb(const char *data, void *context)
{
struct client *client = context;
const char *field_name = data;
unsigned int i, field_name_len;
if (client->auth_passdb_args == NULL)
return NULL;
field_name_len = strlen(field_name);
for (i = 0; client->auth_passdb_args[i] != NULL; i++) {
if (strncmp(client->auth_passdb_args[i], field_name,
field_name_len) == 0 &&
client->auth_passdb_args[i][field_name_len] == '=')
return client->auth_passdb_args[i] + field_name_len+1;
}
return NULL;
}
static const char *
client_get_log_str(struct client *client, const char *msg)
{
static const struct var_expand_func_table func_table[] = {
{ "passdb", client_var_expand_func_passdb },
{ NULL, NULL }
};
static bool expand_error_logged = FALSE;
const struct var_expand_table *var_expand_table;
char *const *e;
const char *error;
string_t *str, *str2;
unsigned int pos;
var_expand_table = get_var_expand_table(client);
str = t_str_new(256);
str2 = t_str_new(128);
for (e = client->set->log_format_elements_split; *e != NULL; e++) {
pos = str_len(str);
if (var_expand_with_funcs(str, *e, var_expand_table,
func_table, client, &error) <= 0 &&
!expand_error_logged) {
i_error("Failed to expand log_format_elements=%s: %s",
*e, error);
expand_error_logged = TRUE;
}
if (have_username_key(*e)) {
/* username is added even if it's empty */
} else {
str_truncate(str2, 0);
if (var_expand(str2, *e, login_var_expand_empty_tab,
&error) <= 0) {
/* we just logged this error above. no need
to do it again. */
}
if (strcmp(str_c(str)+pos, str_c(str2)) == 0) {
/* empty %variables, don't add */
str_truncate(str, pos);
continue;
}
}
if (str_len(str) > 0)
str_append(str, ", ");
}
if (str_len(str) > 0)
str_truncate(str, str_len(str)-2);
const struct var_expand_table tab[3] = {
{ 's', t_strdup(str_c(str)), NULL },
{ '$', msg, NULL },
{ '\0', NULL, NULL }
};
str_truncate(str, 0);
if (var_expand(str, client->set->login_log_format, tab, &error) <= 0) {
i_error("Failed to expand login_log_format=%s: %s",
client->set->login_log_format, error);
expand_error_logged = TRUE;
}
return str_c(str);
}
void client_log(struct client *client, const char *msg)
{
T_BEGIN {
i_info("%s", client_get_log_str(client, msg));
} T_END;
}
void client_log_err(struct client *client, const char *msg)
{
T_BEGIN {
i_error("%s", client_get_log_str(client, msg));
} T_END;
}
void client_log_warn(struct client *client, const char *msg)
{
T_BEGIN {
i_warning("%s", client_get_log_str(client, msg));
} T_END;
}
bool client_is_tls_enabled(struct client *client)
{
return ssl_initialized && strcmp(client->ssl_set->ssl, "no") != 0;
}
const char *client_get_extra_disconnect_reason(struct client *client)
{
unsigned int auth_secs = client->auth_first_started == 0 ? 0 :
ioloop_time - client->auth_first_started;
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy != NULL) {
if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
return "(client sent an invalid cert)";
if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
return "(client didn't send a cert)";
}
if (!client->notified_auth_ready)
return t_strdup_printf(
"(disconnected before auth was ready, waited %u secs)",
(unsigned int)(ioloop_time - client->created));
if (client->auth_attempts == 0) {
return t_strdup_printf("(no auth attempts in %u secs)",
(unsigned int)(ioloop_time - client->created));
}
/* some auth attempts without SSL/TLS */
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy == NULL)
return "(cert required, client didn't start TLS)";
if (client->auth_waiting && client->auth_attempts == 1) {
return t_strdup_printf("(client didn't finish SASL auth, "
"waited %u secs)", auth_secs);
}
if (client->auth_request != NULL && client->auth_attempts == 1) {
return t_strdup_printf("(disconnected while authenticating, "
"waited %u secs)", auth_secs);
}
if (client->authenticating && client->auth_attempts == 1) {
return t_strdup_printf("(disconnected while finishing login, "
"waited %u secs)", auth_secs);
}
if (client->auth_try_aborted && client->auth_attempts == 1)
return "(aborted authentication)";
if (client->auth_process_comm_fail)
return "(auth process communication failure)";
if (client->proxy_auth_failed)
return "(proxy dest auth failed)";
if (client->auth_successes > 0) {
return t_strdup_printf("(internal failure, %u successful auths)",
client->auth_successes);
}
switch (client->last_auth_fail) {
case CLIENT_AUTH_FAIL_CODE_AUTHZFAILED:
return t_strdup_printf(
"(authorization failed, %u attempts in %u secs)",
client->auth_attempts, auth_secs);
case CLIENT_AUTH_FAIL_CODE_TEMPFAIL:
return "(auth service reported temporary failure)";
case CLIENT_AUTH_FAIL_CODE_USER_DISABLED:
return "(user disabled)";
case CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED:
return "(password expired)";
case CLIENT_AUTH_FAIL_CODE_INVALID_BASE64:
return "(sent invalid base64 in response)";
case CLIENT_AUTH_FAIL_CODE_LOGIN_DISABLED:
return "(login disabled)";
case CLIENT_AUTH_FAIL_CODE_MECH_INVALID:
return "(tried to use unsupported auth mechanism)";
case CLIENT_AUTH_FAIL_CODE_MECH_SSL_REQUIRED:
return "(tried to use disallowed plaintext auth)";
default:
break;
}
return t_strdup_printf("(auth failed, %u attempts in %u secs)",
client->auth_attempts, auth_secs);
}
void client_notify_disconnect(struct client *client,
enum client_disconnect_reason reason,
const char *text)
{
if (!client->notified_disconnect) {
if (client->v.notify_disconnect != NULL)
client->v.notify_disconnect(client, reason, text);
client->notified_disconnect = TRUE;
}
}
void client_notify_auth_ready(struct client *client)
{
if (!client->notified_auth_ready) {
if (client->v.notify_auth_ready != NULL)
client->v.notify_auth_ready(client);
client->notified_auth_ready = TRUE;
}
}
void client_notify_status(struct client *client, bool bad, const char *text)
{
if (client->v.notify_status != NULL)
client->v.notify_status(client, bad, text);
}
void client_send_raw_data(struct client *client, const void *data, size_t size)
{
ssize_t ret;
ret = o_stream_send(client->output, data, size);
if (ret < 0 || (size_t)ret != size) {
/* either disconnection or buffer full. in either case we want
this connection destroyed. however destroying it here might
break things if client is still tried to be accessed without
being referenced.. */
i_stream_close(client->input);
}
}
void client_send_raw(struct client *client, const char *data)
{
client_send_raw_data(client, data, strlen(data));
}
bool client_read(struct client *client)
{
switch (i_stream_read(client->input)) {
case -2:
/* buffer full */
client_notify_disconnect(client,
CLIENT_DISCONNECT_RESOURCE_CONSTRAINT,
"Input buffer full, aborting");
client_destroy(client, "Disconnected: Input buffer full");
return FALSE;
case -1:
/* disconnected */
client_destroy(client, "Disconnected");
return FALSE;
case 0:
/* nothing new read */
return TRUE;
default:
/* something was read */
return TRUE;
}
}
void client_input(struct client *client)
{
client->v.input(client);
}