client-common.c revision 434abef12f61881a5cfa28d27193d0854a9639a0
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "login-common.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "hostpid.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "llist.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "istream.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "ostream.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "process-title.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "str.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "str-sanitize.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "safe-memset.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "var-expand.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "master-interface.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "master-service.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "master-auth.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "auth-client.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "login-proxy.h"
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch#include "ssl-proxy.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "client-common.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include <stdlib.h>
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstruct client *clients = NULL, *last_client = NULL;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic unsigned int clients_count = 0;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "Disconnected for inactivity.");
fe779565bda49a0ed0476724819c6e3c1340c94bTimo Sirainen client_destroy(client, "Disconnected: Inactivity");
fe779565bda49a0ed0476724819c6e3c1340c94bTimo Sirainen}
fe779565bda49a0ed0476724819c6e3c1340c94bTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic void client_open_streams(struct client *client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->input =
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->output =
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstruct client *
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenclient_create(int fd, bool ssl, pool_t pool,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct login_settings *set, void **other_sets,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct client *client;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(fd != -1);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client = client_vfuncs.alloc(pool);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->v = client_vfuncs;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->v.auth_send_challenge == NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->v.auth_parse_response == NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->v.auth_parse_response = client_auth_parse_response;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->created = ioloop_time;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->refcount = 1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->pool = pool;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->set = set;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->local_ip = *local_ip;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->ip = *remote_ip;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->fd = fd;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->tls = ssl;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->trusted = client_is_trusted(client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->secured = ssl || client->trusted ||
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen net_ip_compare(remote_ip, local_ip);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (last_client == NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen last_client = client;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen DLLIST_PREPEND(&clients, client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen clients_count++;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->to_disconnect =
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_idle_disconnect_timeout, client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_open_streams(client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->v.create(client, other_sets);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (auth_client_is_connected(auth_client))
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen client->v.send_greeting(client);
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen else
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen client_set_auth_waiting(client);
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen login_refresh_proctitle();
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return client;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschvoid client_destroy(struct client *client, const char *reason)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (client->destroyed)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->destroyed = TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (!client->login_success && reason != NULL) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen reason = t_strconcat(reason, " ",
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_get_extra_disconnect_reason(client), NULL);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (reason != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_log(client, reason);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (last_client == client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen last_client = client->prev;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen DLLIST_REMOVE(&clients, client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->input != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_close(client->input);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->output != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_close(client->output);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->master_tag != 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->auth_request == NULL);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->authenticating);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->refcount > 1);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->authenticating = FALSE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen master_auth_request_abort(master_auth, client->master_tag);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->refcount--;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen } else if (client->auth_request != NULL) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->authenticating);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen sasl_server_auth_abort(client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen } else {
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody i_assert(!client->authenticating);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->io != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen io_remove(&client->io);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->to_disconnect != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen timeout_remove(&client->to_disconnect);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->to_auth_waiting != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen timeout_remove(&client->to_auth_waiting);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->auth_response != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen str_free(&client->auth_response);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->fd != -1) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch net_disconnect(client->fd);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->fd = -1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->proxy_password != NULL) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen safe_memset(client->proxy_password, 0,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen strlen(client->proxy_password));
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free_and_null(client->proxy_password);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->login_proxy != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen login_proxy_free(&client->login_proxy);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->ssl_proxy != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ssl_proxy_free(&client->ssl_proxy);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->v.destroy(client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client_unref(&client) &&
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen master_service_get_service_count(master_service) == 1) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* as soon as this connection is done with proxying
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen (or whatever), the process will die. there's no need for
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen authentication anymore, so close the connection. */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen auth_client_disconnect(auth_client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen login_client_destroyed();
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen login_refresh_proctitle();
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->login_success = TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_destroy(client, reason);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenvoid client_destroy_internal_failure(struct client *client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch "Internal login failure. "
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "Refer to server log for more information.");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_destroy(client, t_strdup_printf(
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "Internal login failure (pid=%s id=%u)",
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen my_pid, client->master_auth_id));
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenvoid client_ref(struct client *client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client->refcount++;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenbool client_unref(struct client **_client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct client *client = *_client;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->refcount > 0);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (--client->refcount > 0)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen *_client = NULL;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->destroyed);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->ssl_proxy == NULL);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(client->login_proxy == NULL);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->input != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_unref(&client->input);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->output != NULL)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_unref(&client->output);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free(client->proxy_user);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free(client->proxy_master_user);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free(client->virtual_user);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free(client->auth_mech_name);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen pool_unref(&client->pool);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_assert(clients_count > 0);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen clients_count--;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen master_service_client_connection_destroyed(master_service);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch login_refresh_proctitle();
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return FALSE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenvoid client_destroy_oldest(void)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct client *client;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (last_client == NULL) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* we have no clients */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* destroy the last client that hasn't successfully authenticated yet.
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen this is usually the last client, but don't kill it if it's just
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen waiting for master to finish its job. */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen for (client = last_client; client != NULL; client = client->prev) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client->master_tag == 0)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen break;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (client == NULL)
8855b8b57050fe3b6dc3f19283488512fae98648Timo Sirainen client = last_client;
8855b8b57050fe3b6dc3f19283488512fae98648Timo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_destroy(client, "Disconnected: Connection queue full");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenvoid clients_destroy_all(void)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct client *client, *next;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch for (client = clients; client != NULL; client = next) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen next = client->next;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_destroy(client, "Disconnected: Shutting down");
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch}
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschstatic void client_start_tls(struct client *client)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch int fd_ssl;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_ref(client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (!client_unref(&client) || client->destroyed)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch fd_ssl = ssl_proxy_alloc(client->fd, &client->ip,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->set, &client->ssl_proxy);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (fd_ssl == -1) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BYE,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "TLS initialization failed.");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_destroy(client,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "Disconnected: TLS initialization failed.");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ssl_proxy_set_client(client->ssl_proxy, client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ssl_proxy_start(client->ssl_proxy);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->starttls = TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->tls = TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->secured = TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch login_refresh_proctitle();
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->fd = fd_ssl;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->io = io_add(client->fd, IO_READ, client_input, client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch i_stream_unref(&client->input);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch o_stream_unref(&client->output);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_open_streams(client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->v.starttls(client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch}
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschstatic int client_output_starttls(struct client *client)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch int ret;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if ((ret = o_stream_flush(client->output)) < 0) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_destroy(client, "Disconnected");
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return 1;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ret > 0) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch o_stream_unset_flush_callback(client->output);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_start_tls(client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return 1;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch}
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschvoid client_cmd_starttls(struct client *client)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (client->tls) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BAD,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch "TLS is already active.");
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (!ssl_initialized) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BAD,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch "TLS support isn't enabled.");
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch /* remove input handler, SSL proxy gives us a new fd. we also have to
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch remove it in case we have to wait for buffer to be flushed */
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (client->io != NULL)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch io_remove(&client->io);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_OK,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "Begin TLS negotiation now.");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* uncork the old fd */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_uncork(client->output);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (o_stream_flush(client->output) <= 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* the buffer has to be flushed */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_set_flush_pending(client->output, TRUE);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_set_flush_callback(client->output,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_output_starttls, client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen } else {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_start_tls(client);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenunsigned int clients_get_count(void)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return clients_count;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic const struct var_expand_table *
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenget_var_expand_table(struct client *client)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen static struct var_expand_table static_tab[] = {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'u', NULL, "user" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'n', NULL, "username" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'd', NULL, "domain" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 's', NULL, "service" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'h', NULL, "home" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'l', NULL, "lip" },
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch { 'r', NULL, "rip" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'p', NULL, "pid" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'm', NULL, "mech" },
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch { 'a', NULL, "lport" },
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen { 'b', NULL, "rport" },
{ 'c', NULL, "secured" },
{ 'k', NULL, "ssl_security" },
{ 'e', NULL, "mail_pid" },
{ '\0', NULL, NULL }
};
struct var_expand_table *tab;
unsigned int i;
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
if (client->virtual_user != NULL) {
tab[0].value = client->virtual_user;
tab[1].value = t_strcut(client->virtual_user, '@');
tab[2].value = strchr(client->virtual_user, '@');
if (tab[2].value != NULL) tab[2].value++;
for (i = 0; i < 3; i++)
tab[i].value = str_sanitize(tab[i].value, 80);
}
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);
return tab;
}
static bool have_key(const struct var_expand_table *table, const char *str)
{
char key;
unsigned int i;
key = var_get_key(str);
for (i = 0; table[i].key != '\0'; i++) {
if (table[i].key == key) {
return table[i].value != NULL &&
table[i].value[0] != '\0';
}
}
return FALSE;
}
static const char *
client_get_log_str(struct client *client, const char *msg)
{
static struct var_expand_table static_tab[3] = {
{ 's', NULL, NULL },
{ '$', NULL, NULL },
{ '\0', NULL, NULL }
};
const struct var_expand_table *var_expand_table;
struct var_expand_table *tab;
const char *p;
char *const *e;
string_t *str;
var_expand_table = get_var_expand_table(client);
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
str = t_str_new(256);
for (e = client->set->log_format_elements_split; *e != NULL; e++) {
for (p = *e; *p != '\0'; p++) {
if (*p != '%' || p[1] == '\0')
continue;
p++;
if (have_key(var_expand_table, p)) {
if (str_len(str) > 0)
str_append(str, ", ");
var_expand(str, *e, var_expand_table);
break;
}
}
}
tab[0].value = t_strdup(str_c(str));
tab[1].value = msg;
str_truncate(str, 0);
var_expand(str, client->set->login_log_format, tab);
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;
}
bool client_is_trusted(struct client *client)
{
const char *const *net;
struct ip_addr net_ip;
unsigned int bits;
if (client->set->login_trusted_networks == NULL)
return FALSE;
net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
for (; *net != NULL; net++) {
if (net_parse_range(*net, &net_ip, &bits) < 0) {
i_error("login_trusted_networks: "
"Invalid network '%s'", *net);
break;
}
if (net_is_in_network(&client->ip, &net_ip, bits))
return TRUE;
}
return FALSE;
}
const char *client_get_extra_disconnect_reason(struct client *client)
{
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->auth_attempts == 0)
return "(no auth attempts)";
/* some auth attempts without SSL/TLS */
if (client->auth_tried_disabled_plaintext)
return "(tried to use disabled plaintext auth)";
if (client->set->auth_ssl_require_client_cert)
return "(cert required, client didn't start TLS)";
if (client->auth_tried_unsupported_mech)
return "(tried to use unsupported auth mechanism)";
if (client->auth_request != NULL && client->auth_attempts == 1)
return "(disconnected while authenticating)";
if (client->auth_try_aborted && client->auth_attempts == 1)
return "(aborted authentication)";
return t_strdup_printf("(auth failed, %u attempts)",
client->auth_attempts);
}
void client_send_line(struct client *client, enum client_cmd_reply reply,
const char *text)
{
client->v.send_line(client, reply, 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_send_line(client, CLIENT_CMD_REPLY_BYE,
"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);
}