client-common.c revision 7c849dbc7be089175c1a83a84ee7249ed695810d
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "login-common.h"
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen#include "hostpid.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "llist.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "istream.h"
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch#include "ostream.h"
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen#include "iostream-rawlog.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "process-title.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "str.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "str-sanitize.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "safe-memset.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "var-expand.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "master-interface.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "master-service.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "master-auth.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "auth-client.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "login-proxy.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "ssl-proxy.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "client-common.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include <stdlib.h>
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct client *clients = NULL, *last_client = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic unsigned int clients_count = 0;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void client_idle_disconnect_timeout(struct client *client)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BYE,
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch "Disconnected for inactivity.");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_destroy(client, "Disconnected: Inactivity");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Boschstatic void client_open_streams(struct client *client)
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch{
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch client->input =
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->output =
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (login_rawlog_dir != NULL) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (iostream_rawlog_create(login_rawlog_dir, &client->input,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch &client->output) < 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch login_rawlog_dir = NULL;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct client *
7384b4e78eaab44693c985192276e31322155e32Stephan Boschclient_create(int fd, bool ssl, pool_t pool,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const struct login_settings *set, void **other_sets,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct client *client;
da300472555d9afdb0bcb767456f731cf5c2f6aaStephan Bosch
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen i_assert(fd != -1);
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client = login_binary->client_vfuncs->alloc(pool);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->v = *login_binary->client_vfuncs;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (client->v.auth_send_challenge == NULL)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->v.auth_send_challenge = client_auth_send_challenge;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (client->v.auth_parse_response == NULL)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->v.auth_parse_response = client_auth_parse_response;
6dad0888fcec8372f230941c70d8940b8c203b32Stephan Bosch
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch client->created = ioloop_time;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch client->refcount = 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->pool = pool;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->set = set;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->local_ip = *local_ip;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen client->ip = *remote_ip;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen client->fd = fd;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->tls = ssl;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->trusted = client_is_trusted(client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->secured = ssl || client->trusted ||
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen net_ip_compare(remote_ip, local_ip);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen if (last_client == NULL)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch last_client = client;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch DLLIST_PREPEND(&clients, client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch clients_count++;
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch client->to_disconnect =
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen client_idle_disconnect_timeout, client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_open_streams(client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->v.create(client, other_sets);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (auth_client_is_connected(auth_client))
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen client->v.send_greeting(client);
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch else
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_set_auth_waiting(client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch login_refresh_proctitle();
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch return client;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen}
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid client_destroy(struct client *client, const char *reason)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (client->destroyed)
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch return;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch client->destroyed = TRUE;
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (!client->login_success && reason != NULL) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch reason = t_strconcat(reason, " ",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_get_extra_disconnect_reason(client), NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (reason != NULL)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_log(client, reason);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (last_client == client)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen last_client = client->prev;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen DLLIST_REMOVE(&clients, client);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client->input != NULL)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_stream_close(client->input);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client->output != NULL)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen o_stream_close(client->output);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client->master_tag != 0) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(client->auth_request == NULL);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(client->authenticating);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(client->refcount > 1);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen client->authenticating = FALSE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen master_auth_request_abort(master_auth, client->master_tag);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen client->refcount--;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen } else if (client->auth_request != NULL) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(client->authenticating);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen sasl_server_auth_abort(client);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen } else {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(!client->authenticating);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client->io != NULL)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen io_remove(&client->io);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client->to_disconnect != NULL)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen timeout_remove(&client->to_disconnect);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client->to_auth_waiting != NULL)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch timeout_remove(&client->to_auth_waiting);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (client->auth_response != NULL)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch str_free(&client->auth_response);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (client->fd != -1) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch net_disconnect(client->fd);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->fd = -1;
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch }
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch if (client->proxy_password != NULL) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch safe_memset(client->proxy_password, 0,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch strlen(client->proxy_password));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_free_and_null(client->proxy_password);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (client->login_proxy != NULL)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch login_proxy_free(&client->login_proxy);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (client->ssl_proxy != NULL)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ssl_proxy_free(&client->ssl_proxy);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client->v.destroy(client);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (client_unref(&client) && initial_service_count == 1) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen /* as soon as this connection is done with proxying
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen (or whatever), the process will die. there's no need for
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch authentication anymore, so close the connection.
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch do this only with initial service_count=1, in case there
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch are other clients with pending authentications */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch auth_client_disconnect(auth_client);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch login_client_destroyed();
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch login_refresh_proctitle();
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschvoid client_destroy_success(struct client *client, const char *reason)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen{
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client->login_success = TRUE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client_destroy(client, reason);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschvoid client_destroy_internal_failure(struct client *client)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch{
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "Internal login failure. "
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "Refer to server log for more information.");
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client_destroy(client, t_strdup_printf(
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "Internal login failure (pid=%s id=%u)",
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch my_pid, client->master_auth_id));
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschvoid client_ref(struct client *client)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch{
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client->refcount++;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschbool client_unref(struct client **_client)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch{
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen struct client *client = *_client;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_assert(client->refcount > 0);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (--client->refcount > 0)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return TRUE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch *_client = NULL;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_assert(client->destroyed);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_assert(client->ssl_proxy == NULL);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_assert(client->login_proxy == NULL);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (client->input != NULL)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_stream_unref(&client->input);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (client->output != NULL)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch o_stream_unref(&client->output);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_free(client->proxy_user);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_free(client->proxy_master_user);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_free(client->virtual_user);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_free(client->auth_mech_name);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch pool_unref(&client->pool);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_assert(clients_count > 0);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch clients_count--;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch master_service_client_connection_destroyed(master_service);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch login_refresh_proctitle();
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen return FALSE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschvoid client_destroy_oldest(void)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch{
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch struct client *client;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (last_client == NULL) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* we have no clients */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* destroy the last client that hasn't successfully authenticated yet.
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch this is usually the last client, but don't kill it if it's just
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch waiting for master to finish its job. */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch for (client = last_client; client != NULL; client = client->prev) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (client->master_tag == 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch break;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen }
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (client == NULL)
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen client = last_client;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_destroy(client, "Disconnected: Connection queue full");
651f981ca065d2365bf2ea07b04318a5402f047aTimo Sirainen}
651f981ca065d2365bf2ea07b04318a5402f047aTimo Sirainen
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainenvoid clients_destroy_all(void)
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct client *client, *next;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch for (client = clients; client != NULL; client = next) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch next = client->next;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen client_destroy(client, "Disconnected: Shutting down");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void client_start_tls(struct client *client)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch int fd_ssl;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client_ref(client);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (!client_unref(&client) || client->destroyed)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen fd_ssl = ssl_proxy_alloc(client->fd, &client->ip,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client->set, &client->ssl_proxy);
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen if (fd_ssl == -1) {
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "TLS initialization failed.");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_destroy(client,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Disconnected: TLS initialization failed.");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch ssl_proxy_set_client(client->ssl_proxy, client);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch ssl_proxy_start(client->ssl_proxy);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch client->starttls = TRUE;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch client->tls = TRUE;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch client->secured = TRUE;
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch login_refresh_proctitle();
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch client->fd = fd_ssl;
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch client->io = io_add(client->fd, IO_READ, client_input, client);
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch i_stream_unref(&client->input);
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch o_stream_unref(&client->output);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch client_open_streams(client);
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch client->v.starttls(client);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch}
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Boschstatic int client_output_starttls(struct client *client)
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch{
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch int ret;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if ((ret = o_stream_flush(client->output)) < 0) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch client_destroy(client, "Disconnected");
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch return 1;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (ret > 0) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch o_stream_unset_flush_callback(client->output);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch client_start_tls(client);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid client_cmd_starttls(struct client *client)
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch{
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (client->tls) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BAD,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "TLS is already active.");
88ead42eefcad4af0313f55275de196f96e8f002Stephan Bosch return;
88ead42eefcad4af0313f55275de196f96e8f002Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (!ssl_initialized) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BAD,
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "TLS support isn't enabled.");
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen return;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch /* remove input handler, SSL proxy gives us a new fd. we also have to
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch remove it in case we have to wait for buffer to be flushed */
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch if (client->io != NULL)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch io_remove(&client->io);
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch client_send_line(client, CLIENT_CMD_REPLY_OK,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Begin TLS negotiation now.");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* uncork the old fd */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_uncork(client->output);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (o_stream_flush(client->output) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* the buffer has to be flushed */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_set_flush_pending(client->output, TRUE);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch o_stream_set_flush_callback(client->output,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch client_output_starttls, client);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch } else {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch client_start_tls(client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Boschunsigned int clients_get_count(void)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return clients_count;
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Boschstatic const struct var_expand_table *
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainenget_var_expand_table(struct client *client)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch static struct var_expand_table static_tab[] = {
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch { 'u', NULL, "user" },
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen { 'n', NULL, "username" },
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch { 'd', NULL, "domain" },
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch { 's', NULL, "service" },
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch { 'h', NULL, "home" },
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch { 'l', NULL, "lip" },
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch { 'r', NULL, "rip" },
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch { 'p', NULL, "pid" },
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch { 'm', NULL, "mech" },
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch { 'a', NULL, "lport" },
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch { 'b', NULL, "rport" },
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch { 'c', NULL, "secured" },
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch { 'k', NULL, "ssl_security" },
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch { 'e', NULL, "mail_pid" },
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch { '\0', NULL, NULL }
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch };
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct var_expand_table *tab;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int i;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch tab = t_malloc(sizeof(static_tab));
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch memcpy(tab, static_tab, sizeof(static_tab));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (client->virtual_user != NULL) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch tab[0].value = client->virtual_user;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch tab[1].value = t_strcut(client->virtual_user, '@');
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch tab[2].value = strchr(client->virtual_user, '@');
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (tab[2].value != NULL) tab[2].value++;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch for (i = 0; i < 3; i++)
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[i].value = str_sanitize(tab[i].value, 80);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[3].value = login_binary->protocol;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[4].value = getenv("HOME");
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[5].value = net_ip2addr(&client->local_ip);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[6].value = net_ip2addr(&client->ip);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[7].value = my_pid;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[8].value = client->auth_mech_name == NULL ? NULL :
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[9].value = dec2str(client->local_port);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[10].value = dec2str(client->remote_port);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (!client->tls) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[11].value = client->secured ? "secured" : NULL;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[12].value = "";
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch } else {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch const char *ssl_state =
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch ssl_proxy_is_handshaked(client->ssl_proxy) ?
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch "TLS" : "TLS handshaking";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *ssl_error =
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen ssl_proxy_get_last_error(client->ssl_proxy);
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch tab[11].value = ssl_error == NULL ? ssl_state :
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch t_strdup_printf("%s: %s", ssl_state, ssl_error);
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch tab[12].value =
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch ssl_proxy_get_security_string(client->ssl_proxy);
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch tab[13].value = client->mail_pid == 0 ? "" :
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch dec2str(client->mail_pid);
e8f1e510df3ab051a816715c2056f0d10aee929eStephan Bosch return tab;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch}
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic bool have_key(const struct var_expand_table *table, const char *str)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
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)
{
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->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_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->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);
}