client-common.c revision e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "login-common.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "hostpid.h"
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen#include "llist.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "istream.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include "ostream.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "iostream-rawlog.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "process-title.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "buffer.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "str.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "base64.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "str-sanitize.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include "safe-memset.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include "var-expand.h"
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen#include "master-interface.h"
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen#include "master-service.h"
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen#include "master-auth.h"
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainen#include "auth-client.h"
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen#include "login-proxy.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include "ssl-proxy.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include "client-common.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include <stdlib.h>
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainenstruct client *clients = NULL, *last_client = NULL;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainenstatic unsigned int clients_count = 0;
ef2b2ef2e6a6eb5e4667f2e63faae8a3b646e8baTimo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen{
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen const char *user_reason, *destroy_reason;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen unsigned int secs;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->master_tag != 0) {
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen secs = ioloop_time - client->auth_finished;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen user_reason = "Timeout while finishing login.";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen destroy_reason = t_strdup_printf(
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen "Timeout while finishing login (waited %u secs)", secs);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_log_err(client, destroy_reason);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (client->auth_request != NULL) {
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen user_reason =
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "Disconnected for inactivity during authentication.";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen destroy_reason =
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen "Disconnected: Inactivity during authentication";
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen } else if (client->login_proxy != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen secs = ioloop_time - client->created;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen user_reason = "Timeout while finishing login.";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen destroy_reason = t_strdup_printf(
4ea6c43a08b37f270bd54b5809142246fd118263Timo Sirainen "proxy: Logging in to %s:%u timed out "
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen "(state=%u, duration=%us)",
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen login_proxy_get_host(client->login_proxy),
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen login_proxy_get_port(client->login_proxy),
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->proxy_state, secs);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_log_err(client, destroy_reason);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen user_reason = "Disconnected for inactivity.";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen destroy_reason = "Disconnected: Inactivity";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
4ea6c43a08b37f270bd54b5809142246fd118263Timo Sirainen client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_destroy(client, destroy_reason);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainenstatic void client_open_streams(struct client *client)
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen{
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen client->input =
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->output =
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (login_rawlog_dir != NULL) {
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen if (iostream_rawlog_create(login_rawlog_dir, &client->input,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen &client->output) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_rawlog_dir = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
7d87a87b360ecac47fe10e7ca5c7e1433dd63004Timo Sirainen}
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic bool client_is_trusted(struct client *client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const char *const *net;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct ip_addr net_ip;
7d87a87b360ecac47fe10e7ca5c7e1433dd63004Timo Sirainen unsigned int bits;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (client->set->login_trusted_networks == NULL)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return FALSE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
ef2b2ef2e6a6eb5e4667f2e63faae8a3b646e8baTimo Sirainen for (; *net != NULL; net++) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (net_parse_range(*net, &net_ip, &bits) < 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_error("login_trusted_networks: "
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "Invalid network '%s'", *net);
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen break;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen }
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen if (net_is_in_network(&client->ip, &net_ip, bits))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return TRUE;
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen }
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen return FALSE;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen}
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainenstruct client *
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainenclient_create(int fd, bool ssl, pool_t pool,
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen const struct login_settings *set, void **other_sets,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct client *client;
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen i_assert(fd != -1);
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen client = login_binary->client_vfuncs->alloc(pool);
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen client->v = *login_binary->client_vfuncs;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->v.auth_send_challenge == NULL)
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen if (client->v.auth_parse_response == NULL)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen client->v.auth_parse_response = client_auth_parse_response;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->created = ioloop_time;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen client->refcount = 1;
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen client->pool = pool;
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen client->set = set;
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen client->local_ip = *local_ip;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen client->ip = *remote_ip;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen client->fd = fd;
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen client->tls = ssl;
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen client->trusted = client_is_trusted(client);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->secured = ssl || client->trusted ||
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen net_ip_compare(remote_ip, local_ip);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->proxy_ttl = LOGIN_PROXY_TTL;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen if (last_client == NULL)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen last_client = client;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen DLLIST_PREPEND(&clients, client);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen clients_count++;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client->to_disconnect =
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client_idle_disconnect_timeout, client);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client_open_streams(client);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client->v.create(client, other_sets);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen if (auth_client_is_connected(auth_client))
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client_notify_auth_ready(client);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen else
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client_set_auth_waiting(client);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen login_refresh_proctitle();
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen return client;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen}
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid client_destroy(struct client *client, const char *reason)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->destroyed)
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen return;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen client->destroyed = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (!client->login_success && reason != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reason = t_strconcat(reason, " ",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_get_extra_disconnect_reason(client), NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen if (reason != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_log(client, reason);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (last_client == client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen last_client = client->prev;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen DLLIST_REMOVE(&clients, client);
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (client->input != NULL)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen i_stream_close(client->input);
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen if (client->output != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen o_stream_close(client->output);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->master_tag != 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(client->auth_request == NULL);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_assert(client->authenticating);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(client->refcount > 1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->authenticating = FALSE;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen master_auth_request_abort(master_auth, client->master_tag);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen client->refcount--;
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen } else if (client->auth_request != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(client->authenticating);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sasl_server_auth_abort(client);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(!client->authenticating);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (client->io != NULL)
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen io_remove(&client->io);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->to_disconnect != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_remove(&client->to_disconnect);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->to_auth_waiting != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_remove(&client->to_auth_waiting);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen if (client->auth_response != NULL)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen str_free(&client->auth_response);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->fd != -1) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen net_disconnect(client->fd);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen client->fd = -1;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (client->proxy_password != NULL) {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen safe_memset(client->proxy_password, 0,
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen strlen(client->proxy_password));
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen i_free_and_null(client->proxy_password);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen }
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->login_proxy != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_proxy_free(&client->login_proxy);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->v.destroy(client);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client_unref(&client) && initial_service_count == 1) {
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen /* as soon as this connection is done with proxying
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen (or whatever), the process will die. there's no need for
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen authentication anymore, so close the connection.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen do this only with initial service_count=1, in case there
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen are other clients with pending authentications */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen auth_client_disconnect(auth_client, "unnecessary connection");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_client_destroyed();
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_refresh_proctitle();
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen{
2c719bcb92302f45df4badb71d1d97f57235d0ccTimo Sirainen client->login_success = TRUE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen client_destroy(client, reason);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenvoid client_destroy_internal_failure(struct client *client)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "Internal login failure. "
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen "Refer to server log for more information.");
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen client_destroy(client, t_strdup_printf(
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen "Internal login failure (pid=%s id=%u)",
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen my_pid, client->master_auth_id));
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen}
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainenvoid client_ref(struct client *client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->refcount++;
f8da06de93e28b5d3e039a427cdde7e1e15daec8Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenbool client_unref(struct client **_client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct client *client = *_client;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen i_assert(client->refcount > 0);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (--client->refcount > 0)
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen return TRUE;
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen
8552b0cad8ffe9ccb8270577ba28b8010c89af11Timo Sirainen *_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->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_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 (!ssl_initialized) {
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 >> 16) & 0xff);
#ifdef HAVE_IPV6
if (IPADDR_IS_V6(&client->ip))
buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6));
else
#endif
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, NULL }
};
static const struct var_expand_table *
get_var_expand_table(struct client *client)
{
struct var_expand_table *tab;
unsigned int i;
tab = t_malloc(sizeof(login_var_expand_empty_tab));
memcpy(tab, login_var_expand_empty_tab,
sizeof(login_var_expand_empty_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);
tab[14].value = client_get_session_id(client);
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_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;
char *const *e;
string_t *str, *str2;
unsigned int pos;
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);
str2 = t_str_new(128);
for (e = client->set->log_format_elements_split; *e != NULL; e++) {
pos = str_len(str);
var_expand(str, *e, var_expand_table);
if (have_username_key(*e)) {
/* username is added even if it's empty */
} else {
str_truncate(str2, 0);
var_expand(str2, *e, login_var_expand_empty_tab);
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);
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;
}
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->auth_tried_disabled_plaintext)
return "(tried to use disallowed 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);
}
if (client->auth_user_disabled)
return "(user disabled)";
if (client->auth_pass_expired)
return "(password expired)";
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) {
client->v.notify_disconnect(client, reason, text);
client->notified_disconnect = TRUE;
}
}
void client_notify_auth_ready(struct client *client)
{
if (!client->notified_auth_ready) {
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);
}