client-common.c revision baebb412a9a5a44b1756e01cfa3b99f5d8a846b6
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "login-common.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "hostpid.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "llist.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "istream.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "ostream.h"
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen#include "iostream-rawlog.h"
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainen#include "process-title.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "str.h"
18565c69efcd7db003dbf27cf625ed822e889fb1Timo Sirainen#include "str-sanitize.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "safe-memset.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "var-expand.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "master-interface.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "master-service.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "master-auth.h"
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen#include "auth-client.h"
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen#include "login-proxy.h"
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen#include "ssl-proxy.h"
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen#include "client-common.h"
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen#include <stdlib.h>
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainenstruct client *clients = NULL, *last_client = NULL;
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainenstatic unsigned int clients_count = 0;
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen{
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen "Disconnected for inactivity.");
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen client_destroy(client, "Disconnected: Inactivity");
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
af1f4b17a92ca7b2661737e65c7849df289d3070Timo Sirainenstatic void client_open_streams(struct client *client)
af1f4b17a92ca7b2661737e65c7849df289d3070Timo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen client->input =
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen client->output =
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (login_rawlog_dir != NULL) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (iostream_rawlog_create(login_rawlog_dir, &client->input,
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen &client->output) < 0)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen login_rawlog_dir = NULL;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainenstruct client *
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainenclient_create(int fd, bool ssl, pool_t pool,
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen const struct login_settings *set, void **other_sets,
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct client *client;
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen i_assert(fd != -1);
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen client = login_binary->client_vfuncs->alloc(pool);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen client->v = *login_binary->client_vfuncs;
597dce34068d603fb759b4dff404b34049213e51Timo Sirainen if (client->v.auth_send_challenge == NULL)
597dce34068d603fb759b4dff404b34049213e51Timo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
597dce34068d603fb759b4dff404b34049213e51Timo Sirainen if (client->v.auth_parse_response == NULL)
597dce34068d603fb759b4dff404b34049213e51Timo Sirainen client->v.auth_parse_response = client_auth_parse_response;
597dce34068d603fb759b4dff404b34049213e51Timo Sirainen
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainen client->created = ioloop_time;
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainen client->refcount = 1;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainen client->pool = pool;
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainen client->set = set;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen client->local_ip = *local_ip;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client->ip = *remote_ip;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen client->fd = fd;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen client->tls = ssl;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client->trusted = client_is_trusted(client);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen client->secured = ssl || client->trusted ||
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen net_ip_compare(remote_ip, local_ip);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen if (last_client == NULL)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen last_client = client;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen DLLIST_PREPEND(&clients, client);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen clients_count++;
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen client->to_disconnect =
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
7f773564b94e6054a40d3785cb63c29f1e4d4deeTimo Sirainen client_idle_disconnect_timeout, client);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen client_open_streams(client);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client->v.create(client, other_sets);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (auth_client_is_connected(auth_client))
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client->v.send_greeting(client);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen else
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client_set_auth_waiting(client);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen login_refresh_proctitle();
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return client;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenvoid client_destroy(struct client *client, const char *reason)
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (client->destroyed)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen client->destroyed = TRUE;
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen if (!client->login_success && reason != NULL) {
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen reason = t_strconcat(reason, " ",
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen client_get_extra_disconnect_reason(client), NULL);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen if (reason != NULL)
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen client_log(client, reason);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (last_client == client)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen last_client = client->prev;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen DLLIST_REMOVE(&clients, client);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (client->input != NULL)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_stream_close(client->input);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (client->output != NULL)
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen o_stream_close(client->output);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (client->master_tag != 0) {
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen i_assert(client->auth_request == NULL);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_assert(client->authenticating);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen i_assert(client->refcount > 1);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen client->authenticating = FALSE;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen master_auth_request_abort(master_auth, client->master_tag);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client->refcount--;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen } else if (client->auth_request != NULL) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen i_assert(client->authenticating);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen sasl_server_auth_abort(client);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen } else {
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen i_assert(!client->authenticating);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen }
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen if (client->io != NULL)
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen io_remove(&client->io);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen if (client->to_disconnect != NULL)
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen timeout_remove(&client->to_disconnect);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen if (client->to_auth_waiting != NULL)
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen timeout_remove(&client->to_auth_waiting);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen if (client->auth_response != NULL)
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen str_free(&client->auth_response);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen if (client->fd != -1) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen net_disconnect(client->fd);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client->fd = -1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (client->proxy_password != NULL) {
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen safe_memset(client->proxy_password, 0,
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen strlen(client->proxy_password));
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen i_free_and_null(client->proxy_password);
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen }
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen if (client->login_proxy != NULL)
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen login_proxy_free(&client->login_proxy);
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen if (client->ssl_proxy != NULL)
a1808be0774cbcb28fec45341aabf803ec44bae5Timo Sirainen ssl_proxy_free(&client->ssl_proxy);
a1808be0774cbcb28fec45341aabf803ec44bae5Timo Sirainen client->v.destroy(client);
a1808be0774cbcb28fec45341aabf803ec44bae5Timo Sirainen if (client_unref(&client) &&
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen master_service_get_service_count(master_service) == 1) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* as soon as this connection is done with proxying
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen (or whatever), the process will die. there's no need for
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen authentication anymore, so close the connection. */
c74ea62a27878910e3ca1614ca055d7e2b3b00d5Timo Sirainen auth_client_disconnect(auth_client);
c74ea62a27878910e3ca1614ca055d7e2b3b00d5Timo Sirainen }
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen login_client_destroyed();
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen login_refresh_proctitle();
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen}
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen{
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen client->login_success = TRUE;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client_destroy(client, reason);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenvoid client_destroy_internal_failure(struct client *client)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen "Internal login failure. "
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen "Refer to server log for more information.");
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen client_destroy(client, t_strdup_printf(
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen "Internal login failure (pid=%s id=%u)",
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen my_pid, client->master_auth_id));
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenvoid client_ref(struct client *client)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen client->refcount++;
18565c69efcd7db003dbf27cf625ed822e889fb1Timo Sirainen}
18565c69efcd7db003dbf27cf625ed822e889fb1Timo Sirainen
18565c69efcd7db003dbf27cf625ed822e889fb1Timo Sirainenbool client_unref(struct client **_client)
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen{
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen struct client *client = *_client;
18565c69efcd7db003dbf27cf625ed822e889fb1Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(client->refcount > 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (--client->refcount > 0)
18565c69efcd7db003dbf27cf625ed822e889fb1Timo Sirainen return TRUE;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen *_client = NULL;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_assert(client->destroyed);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_assert(client->ssl_proxy == NULL);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(client->login_proxy == NULL);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (client->input != NULL)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_stream_unref(&client->input);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (client->output != NULL)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen o_stream_unref(&client->output);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_free(client->proxy_user);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_free(client->proxy_master_user);
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen i_free(client->virtual_user);
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen i_free(client->auth_mech_name);
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen pool_unref(&client->pool);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen i_assert(clients_count > 0);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen clients_count--;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen master_service_client_connection_destroyed(master_service);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen login_refresh_proctitle();
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return FALSE;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
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_destroy(client, "Disconnected: Connection queue full");
}
void clients_destroy_all(void)
{
struct client *client, *next;
for (client = clients; client != NULL; client = next) {
next = client->next;
client_destroy(client, "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->set, &client->ssl_proxy);
if (fd_ssl == -1) {
client_send_line(client, CLIENT_CMD_REPLY_BYE,
"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_send_line(client, CLIENT_CMD_REPLY_BAD,
"TLS is already active.");
return;
}
if (!ssl_initialized) {
client_send_line(client, CLIENT_CMD_REPLY_BAD,
"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_send_line(client, CLIENT_CMD_REPLY_OK,
"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;
}
static const struct var_expand_table *
get_var_expand_table(struct client *client)
{
static struct var_expand_table static_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, NULL }
};
struct var_expand_table *tab;
unsigned int i;
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
if (client->virtual_user != NULL) {
tab[0].value = client->virtual_user;
tab[1].value = t_strcut(client->virtual_user, '@');
tab[2].value = strchr(client->virtual_user, '@');
if (tab[2].value != NULL) tab[2].value++;
for (i = 0; i < 3; i++)
tab[i].value = str_sanitize(tab[i].value, 80);
}
tab[3].value = login_binary->protocol;
tab[4].value = getenv("HOME");
tab[5].value = net_ip2addr(&client->local_ip);
tab[6].value = net_ip2addr(&client->ip);
tab[7].value = my_pid;
tab[8].value = client->auth_mech_name == NULL ? NULL :
str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
tab[9].value = dec2str(client->local_port);
tab[10].value = dec2str(client->remote_port);
if (!client->tls) {
tab[11].value = client->secured ? "secured" : NULL;
tab[12].value = "";
} else {
const char *ssl_state =
ssl_proxy_is_handshaked(client->ssl_proxy) ?
"TLS" : "TLS handshaking";
const char *ssl_error =
ssl_proxy_get_last_error(client->ssl_proxy);
tab[11].value = ssl_error == NULL ? ssl_state :
t_strdup_printf("%s: %s", ssl_state, ssl_error);
tab[12].value =
ssl_proxy_get_security_string(client->ssl_proxy);
}
tab[13].value = client->mail_pid == 0 ? "" :
dec2str(client->mail_pid);
return tab;
}
static bool have_key(const struct var_expand_table *table, const char *str)
{
char key;
unsigned int i;
key = var_get_key(str);
for (i = 0; table[i].key != '\0'; i++) {
if (table[i].key == key) {
return table[i].value != NULL &&
table[i].value[0] != '\0';
}
}
return FALSE;
}
static const char *
client_get_log_str(struct client *client, const char *msg)
{
static struct var_expand_table static_tab[3] = {
{ 's', NULL, NULL },
{ '$', NULL, NULL },
{ '\0', NULL, NULL }
};
const struct var_expand_table *var_expand_table;
struct var_expand_table *tab;
const char *p;
char *const *e;
string_t *str;
var_expand_table = get_var_expand_table(client);
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
str = t_str_new(256);
for (e = client->set->log_format_elements_split; *e != NULL; e++) {
for (p = *e; *p != '\0'; p++) {
if (*p != '%' || p[1] == '\0')
continue;
p++;
if (have_key(var_expand_table, p)) {
if (str_len(str) > 0)
str_append(str, ", ");
var_expand(str, *e, var_expand_table);
break;
}
}
}
tab[0].value = t_strdup(str_c(str));
tab[1].value = msg;
str_truncate(str, 0);
var_expand(str, client->set->login_log_format, tab);
return str_c(str);
}
void client_log(struct client *client, const char *msg)
{
T_BEGIN {
i_info("%s", client_get_log_str(client, msg));
} T_END;
}
void client_log_err(struct client *client, const char *msg)
{
T_BEGIN {
i_error("%s", client_get_log_str(client, msg));
} T_END;
}
bool client_is_trusted(struct client *client)
{
const char *const *net;
struct ip_addr net_ip;
unsigned int bits;
if (client->set->login_trusted_networks == NULL)
return FALSE;
net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
for (; *net != NULL; net++) {
if (net_parse_range(*net, &net_ip, &bits) < 0) {
i_error("login_trusted_networks: "
"Invalid network '%s'", *net);
break;
}
if (net_is_in_network(&client->ip, &net_ip, bits))
return TRUE;
}
return FALSE;
}
const char *client_get_extra_disconnect_reason(struct client *client)
{
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy != NULL) {
if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
return "(client sent an invalid cert)";
if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
return "(client didn't send a cert)";
}
if (client->auth_attempts == 0)
return "(no auth attempts)";
/* some auth attempts without SSL/TLS */
if (client->auth_tried_disabled_plaintext)
return "(tried to use disabled plaintext auth)";
if (client->set->auth_ssl_require_client_cert &&
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 "(disconnected while authenticating)";
if (client->auth_try_aborted && client->auth_attempts == 1)
return "(aborted authentication)";
return t_strdup_printf("(auth failed, %u attempts)",
client->auth_attempts);
}
void client_send_line(struct client *client, enum client_cmd_reply reply,
const char *text)
{
client->v.send_line(client, reply, text);
}
void client_send_raw_data(struct client *client, const void *data, size_t size)
{
ssize_t ret;
ret = o_stream_send(client->output, data, size);
if (ret < 0 || (size_t)ret != size) {
/* either disconnection or buffer full. in either case we want
this connection destroyed. however destroying it here might
break things if client is still tried to be accessed without
being referenced.. */
i_stream_close(client->input);
}
}
void client_send_raw(struct client *client, const char *data)
{
client_send_raw_data(client, data, strlen(data));
}
bool client_read(struct client *client)
{
switch (i_stream_read(client->input)) {
case -2:
/* buffer full */
client_send_line(client, CLIENT_CMD_REPLY_BYE,
"Input buffer full, aborting");
client_destroy(client, "Disconnected: Input buffer full");
return FALSE;
case -1:
/* disconnected */
client_destroy(client, "Disconnected");
return FALSE;
case 0:
/* nothing new read */
return TRUE;
default:
/* something was read */
return TRUE;
}
}
void client_input(struct client *client)
{
client->v.input(client);
}