client-common.c revision 5f5870385cff47efd2f58e7892f251cf13761528
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "login-common.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "hostpid.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "llist.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "istream.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "ostream.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "iostream-rawlog.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "process-title.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "str.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "str-sanitize.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "safe-memset.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "var-expand.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "master-interface.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "master-service.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "master-auth.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "auth-client.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "login-proxy.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "ssl-proxy.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include "client-common.h"
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen#include <stdlib.h>
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainenstruct client *clients = NULL, *last_client = NULL;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenstatic unsigned int clients_count = 0;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "Disconnected for inactivity.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client, "Disconnected: Inactivity");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenstatic void client_open_streams(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->input =
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->output =
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (login_rawlog_dir != NULL) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (iostream_rawlog_create(login_rawlog_dir, &client->input,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen &client->output) < 0)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_rawlog_dir = NULL;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenstruct client *
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenclient_create(int fd, bool ssl, pool_t pool,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen const struct login_settings *set, void **other_sets,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen struct client *client;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(fd != -1);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client = login_binary->client_vfuncs->alloc(pool);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v = *login_binary->client_vfuncs;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->v.auth_send_challenge == NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->v.auth_parse_response == NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v.auth_parse_response = client_auth_parse_response;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->created = ioloop_time;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->refcount = 1;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->pool = pool;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->set = set;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->local_ip = *local_ip;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen client->ip = *remote_ip;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->fd = fd;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->tls = ssl;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen client->trusted = client_is_trusted(client);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen client->secured = ssl || client->trusted ||
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen net_ip_compare(remote_ip, local_ip);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (last_client == NULL)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen last_client = client;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen DLLIST_PREPEND(&clients, client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen clients_count++;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->to_disconnect =
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_idle_disconnect_timeout, client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_open_streams(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v.create(client, other_sets);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (auth_client_is_connected(auth_client))
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v.send_greeting(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen else
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_set_auth_waiting(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_refresh_proctitle();
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return client;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid client_destroy(struct client *client, const char *reason)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->destroyed)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->destroyed = TRUE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (!client->login_success && reason != NULL) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen reason = t_strconcat(reason, " ",
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_get_extra_disconnect_reason(client), NULL);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (reason != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_log(client, reason);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (last_client == client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen last_client = client->prev;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen DLLIST_REMOVE(&clients, client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->input != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_stream_close(client->input);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->output != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_close(client->output);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->master_tag != 0) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->auth_request == NULL);
f67c2a7415cbd1a783920cc84ded731dcafa4ffcTimo Sirainen i_assert(client->authenticating);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->refcount > 1);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->authenticating = FALSE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen master_auth_request_abort(master_auth, client->master_tag);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->refcount--;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen } else if (client->auth_request != NULL) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->authenticating);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen sasl_server_auth_abort(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen } else {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(!client->authenticating);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (client->io != NULL)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen io_remove(&client->io);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (client->to_disconnect != NULL)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen timeout_remove(&client->to_disconnect);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (client->to_auth_waiting != NULL)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen timeout_remove(&client->to_auth_waiting);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (client->auth_response != NULL)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen str_free(&client->auth_response);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (client->fd != -1) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen net_disconnect(client->fd);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen client->fd = -1;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen }
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (client->proxy_password != NULL) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen safe_memset(client->proxy_password, 0,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen strlen(client->proxy_password));
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_free_and_null(client->proxy_password);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->login_proxy != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_proxy_free(&client->login_proxy);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->ssl_proxy != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen ssl_proxy_free(&client->ssl_proxy);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v.destroy(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client_unref(&client) && initial_service_count == 1) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen /* as soon as this connection is done with proxying
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen (or whatever), the process will die. there's no need for
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen authentication anymore, so close the connection.
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen do this only with initial service_count=1, in case there
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen are other clients with pending authentications */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen auth_client_disconnect(auth_client, "unnecessary connection");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_client_destroyed();
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_refresh_proctitle();
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->login_success = TRUE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client, reason);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid client_destroy_internal_failure(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "Internal login failure. "
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "Refer to server log for more information.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client, t_strdup_printf(
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "Internal login failure (pid=%s id=%u)",
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen my_pid, client->master_auth_id));
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid client_ref(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->refcount++;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenbool client_unref(struct client **_client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen struct client *client = *_client;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->refcount > 0);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (--client->refcount > 0)
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen return TRUE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen *_client = NULL;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->destroyed);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->ssl_proxy == NULL);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(client->login_proxy == NULL);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->input != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_stream_unref(&client->input);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->output != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_unref(&client->output);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_free(client->proxy_user);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_free(client->proxy_master_user);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_free(client->virtual_user);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_free(client->auth_mech_name);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen pool_unref(&client->pool);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_assert(clients_count > 0);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen clients_count--;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen master_service_client_connection_destroyed(master_service);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_refresh_proctitle();
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return FALSE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid client_destroy_oldest(void)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen struct client *client;
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen if (last_client == NULL) {
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen /* we have no clients */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen /* destroy the last client that hasn't successfully authenticated yet.
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen this is usually the last client, but don't kill it if it's just
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen waiting for master to finish its job. */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen for (client = last_client; client != NULL; client = client->prev) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->master_tag == 0)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen break;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client == NULL)
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen client = last_client;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client, "Disconnected: Connection queue full");
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid clients_destroy_all(void)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen struct client *client, *next;
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen for (client = clients; client != NULL; client = next) {
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen next = client->next;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client, "Disconnected: Shutting down");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainenstatic void client_start_tls(struct client *client)
e9fdf11b707cbff4686d742a74c261eda4a4a92aTimo Sirainen{
49b6ea58565e72b44b1e5cee6fb7619d54150347Timo Sirainen int fd_ssl;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_ref(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (!client_unref(&client) || client->destroyed)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen fd_ssl = ssl_proxy_alloc(client->fd, &client->ip,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->set, &client->ssl_proxy);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (fd_ssl == -1) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "TLS initialization failed.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "Disconnected: TLS initialization failed.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return;
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen ssl_proxy_set_client(client->ssl_proxy, client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen ssl_proxy_start(client->ssl_proxy);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->starttls = TRUE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->tls = TRUE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->secured = TRUE;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen login_refresh_proctitle();
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->fd = fd_ssl;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen i_stream_unref(&client->input);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_unref(&client->output);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_open_streams(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client->v.starttls(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenstatic int client_output_starttls(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen int ret;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_destroy(client, "Disconnected");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return 1;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (ret > 0) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_unset_flush_callback(client->output);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_start_tls(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return 1;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenvoid client_cmd_starttls(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->tls) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "TLS is already active.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (!ssl_initialized) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "TLS support isn't enabled.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen remove it in case we have to wait for buffer to be flushed */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (client->io != NULL)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen io_remove(&client->io);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_OK,
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen "Begin TLS negotiation now.");
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen /* uncork the old fd */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_uncork(client->output);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen if (o_stream_flush(client->output) <= 0) {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen /* the buffer has to be flushed */
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_set_flush_pending(client->output, TRUE);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen o_stream_set_flush_callback(client->output,
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen client_output_starttls, client);
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen } else {
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen client_start_tls(client);
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen }
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenunsigned int clients_get_count(void)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen return clients_count;
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen}
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenstatic const struct var_expand_table *
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainenget_var_expand_table(struct client *client)
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen{
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen static struct var_expand_table static_tab[] = {
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen { 'u', NULL, "user" },
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen { 'n', NULL, "username" },
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen { 'd', NULL, "domain" },
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen { 's', NULL, "service" },
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen { 'h', NULL, "home" },
7313101ef0723c7b210e7b7bb5eb11d0d73d1b4dTimo Sirainen { '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, 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) {
if (table[i].value == NULL)
return FALSE;
if (table[i].value[0] != '\0')
return TRUE;
/* "" key - hide except in username */
return key == 'u' || key == 'n';
}
}
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;
}
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_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)
{
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->greeting_sent)
return t_strdup_printf(
"(disconnected before greeting, 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->auth_tried_disabled_plaintext)
return "(tried to use disabled plaintext auth)";
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy == NULL)
return "(cert required, client didn't start TLS)";
if (client->auth_tried_unsupported_mech)
return "(tried to use unsupported auth mechanism)";
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 succesful auths)",
client->auth_successes);
}
return t_strdup_printf("(auth failed, %u attempts in %u secs)",
client->auth_attempts, auth_secs);
}
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);
}