client-common.c revision 506e41a4efbc7f4bba93cd295ca4dba18ed3cf09
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "login-common.h"
35316602eabbae7dcb86dd74c71e04cce45ba7c7Timo Sirainen#include "hostpid.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "llist.h"
27bc15088a485a8047fca9b0d24d2904c6dda919Timo Sirainen#include "istream.h"
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen#include "ostream.h"
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen#include "process-title.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str-sanitize.h"
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen#include "safe-memset.h"
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen#include "var-expand.h"
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen#include "master-interface.h"
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen#include "master-service.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "master-auth.h"
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen#include "auth-client.h"
2674b4f0cf8f3c203d8e56b29735f5e267038dafTimo Sirainen#include "login-proxy.h"
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen#include "ssl-proxy.h"
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen#include "client-common.h"
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen#include <stdlib.h>
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainenstruct client *clients = NULL, *last_client = NULL;
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainenstatic unsigned int clients_count = 0;
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen{
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen "Disconnected for inactivity.");
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen client_destroy(client, "Disconnected: Inactivity");
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen}
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainenstatic void client_open_streams(struct client *client)
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen{
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen client->input =
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen client->output =
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen}
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainenstruct client *
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainenclient_create(int fd, bool ssl, pool_t pool,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen const struct login_settings *set, void **other_sets,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct client *client;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen i_assert(fd != -1);
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen client = client_vfuncs.alloc(pool);
741d705983e10046f07ef372b760bcdd169b068aTimo Sirainen client->v = client_vfuncs;
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen if (client->v.auth_send_challenge == NULL)
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen if (client->v.auth_parse_response == NULL)
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen client->v.auth_parse_response = client_auth_parse_response;
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen client->created = ioloop_time;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen client->refcount = 1;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainen client->pool = pool;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->set = set;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->local_ip = *local_ip;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->ip = *remote_ip;
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen client->fd = fd;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->tls = ssl;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->trusted = client_is_trusted(client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->secured = ssl || client->trusted ||
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen net_ip_compare(remote_ip, local_ip);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen if (last_client == NULL)
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen last_client = client;
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen DLLIST_PREPEND(&clients, client);
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen clients_count++;
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen client->to_disconnect =
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client_idle_disconnect_timeout, client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client_open_streams(client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->v.create(client, other_sets);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (auth_client_is_connected(auth_client))
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen client->v.send_greeting(client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen else
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client_set_auth_waiting(client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen login_refresh_proctitle();
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen return client;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen}
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainenvoid client_destroy(struct client *client, const char *reason)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen{
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->destroyed)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen return;
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen client->destroyed = TRUE;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (!client->login_success && reason != NULL) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen reason = t_strconcat(reason, " ",
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client_get_extra_disconnect_reason(client), NULL);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (reason != NULL)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client_log(client, reason);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (last_client == client)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen last_client = client->prev;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen DLLIST_REMOVE(&clients, client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen if (client->input != NULL)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen i_stream_close(client->input);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->output != NULL)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen o_stream_close(client->output);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->master_tag != 0) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_assert(client->auth_request == NULL);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_assert(client->authenticating);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_assert(client->refcount > 1);
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen client->authenticating = FALSE;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen master_auth_request_abort(master_auth, client->master_tag);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->refcount--;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen } else if (client->auth_request != NULL) {
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen i_assert(client->authenticating);
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen sasl_server_auth_abort(client);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen } else {
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen i_assert(!client->authenticating);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen }
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->io != NULL)
40a5aeebf6b4858b93f0ddff0bf12fba769cf903Timo Sirainen io_remove(&client->io);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->to_disconnect != NULL)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen timeout_remove(&client->to_disconnect);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->to_auth_waiting != NULL)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen timeout_remove(&client->to_auth_waiting);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (client->auth_response != NULL)
40a5aeebf6b4858b93f0ddff0bf12fba769cf903Timo Sirainen str_free(&client->auth_response);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->fd != -1) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen net_disconnect(client->fd);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen client->fd = -1;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
c5794838af9995f50bfecb06a3cd4f9a0ac77858Timo Sirainen if (client->proxy_password != NULL) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen safe_memset(client->proxy_password, 0,
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen strlen(client->proxy_password));
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_free_and_null(client->proxy_password);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (client->login_proxy != NULL)
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainen login_proxy_free(&client->login_proxy);
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen if (client->ssl_proxy != NULL)
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen ssl_proxy_free(&client->ssl_proxy);
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen client->v.destroy(client);
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen if (client_unref(&client) &&
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen master_service_get_service_count(master_service) == 1) {
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen /* as soon as this connection is done with proxying
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen (or whatever), the process will die. there's no need for
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen authentication anymore, so close the connection. */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen auth_client_disconnect(auth_client);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen login_client_destroyed();
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen login_refresh_proctitle();
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen}
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen{
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen client->login_success = TRUE;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen client_destroy(client, reason);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen}
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid client_destroy_internal_failure(struct client *client)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen{
61ddcdc28f50d9cb9994fcc4ad63f9dff0e80628Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Internal login failure. "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Refer to server log for more information.");
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen client_destroy(client, "Internal login failure");
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen}
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen
61ddcdc28f50d9cb9994fcc4ad63f9dff0e80628Timo Sirainenvoid client_ref(struct client *client)
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen{
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen client->refcount++;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen}
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenbool client_unref(struct client **_client)
61ddcdc28f50d9cb9994fcc4ad63f9dff0e80628Timo Sirainen{
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen struct client *client = *_client;
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen i_assert(client->refcount > 0);
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen if (--client->refcount > 0)
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen return TRUE;
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen *_client = NULL;
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen i_assert(client->destroyed);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen i_assert(client->ssl_proxy == NULL);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_assert(client->login_proxy == NULL);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
1f68b6db9b8d02b0f4116e42ac82c4aac5579574Timo Sirainen if (client->input != NULL)
1f68b6db9b8d02b0f4116e42ac82c4aac5579574Timo Sirainen i_stream_unref(&client->input);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (client->output != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_unref(&client->output);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(client->proxy_user);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen i_free(client->proxy_master_user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(client->virtual_user);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen i_free(client->auth_mech_name);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen pool_unref(&client->pool);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen i_assert(clients_count > 0);
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen clients_count--;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen master_service_client_connection_destroyed(master_service);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen login_refresh_proctitle();
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen return FALSE;
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen}
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainenvoid client_destroy_oldest(void)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct client *client;
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen if (last_client == NULL) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* we have no clients */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* destroy the last client that hasn't successfully authenticated yet.
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen this is usually the last client, but don't kill it if it's just
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen waiting for master to finish its job. */
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen for (client = last_client; client != NULL; client = client->prev) {
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen if (client->master_tag == 0)
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen break;
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen }
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen if (client == NULL)
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen client = last_client;
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_destroy(client, "Disconnected: Connection queue full");
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainenvoid clients_destroy_all(void)
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen{
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen struct client *client, *next;
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen for (client = clients; client != NULL; client = next) {
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen next = client->next;
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen client_destroy(client, "Disconnected: Shutting down");
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstatic void client_start_tls(struct client *client)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen int fd_ssl;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_ref(client);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (!client_unref(&client) || client->destroyed)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainen fd_ssl = ssl_proxy_alloc(client->fd, &client->ip,
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen client->set, &client->ssl_proxy);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (fd_ssl == -1) {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen "TLS initialization failed.");
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_destroy(client,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen "Disconnected: TLS initialization failed.");
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen ssl_proxy_set_client(client->ssl_proxy, client);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen ssl_proxy_start(client->ssl_proxy);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->starttls = TRUE;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->tls = TRUE;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->secured = TRUE;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen login_refresh_proctitle();
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->fd = fd_ssl;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen i_stream_unref(&client->input);
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen o_stream_unref(&client->output);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_open_streams(client);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->v.starttls(client);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
439942f89a77180719644e7af3752a8329259eb9Timo Sirainenstatic int client_output_starttls(struct client *client)
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen{
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen int ret;
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_destroy(client, "Disconnected");
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen return 1;
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (ret > 0) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen o_stream_unset_flush_callback(client->output);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_start_tls(client);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
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->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->ssl_require_client_cert)
return "(cert required, client didn't start TLS)";
if (client->auth_tried_unsupported_mech)
return "(tried to use unsupported auth mechanism)";
if (client->auth_request != NULL && client->auth_attempts == 1)
return "(disconnected while authenticating)";
if (client->auth_try_aborted && client->auth_attempts == 1)
return "(aborted authentication)";
return t_strdup_printf("(auth failed, %u attempts)",
client->auth_attempts);
}
void client_send_line(struct client *client, enum client_cmd_reply reply,
const char *text)
{
client->v.send_line(client, reply, text);
}
void client_send_raw_data(struct client *client, const void *data, size_t size)
{
ssize_t ret;
ret = o_stream_send(client->output, data, size);
if (ret < 0 || (size_t)ret != size) {
/* either disconnection or buffer full. in either case we want
this connection destroyed. however destroying it here might
break things if client is still tried to be accessed without
being referenced.. */
i_stream_close(client->input);
}
}
void client_send_raw(struct client *client, const char *data)
{
client_send_raw_data(client, data, strlen(data));
}
bool client_read(struct client *client)
{
switch (i_stream_read(client->input)) {
case -2:
/* buffer full */
client_send_line(client, CLIENT_CMD_REPLY_BYE,
"Input buffer full, aborting");
client_destroy(client, "Disconnected: Input buffer full");
return FALSE;
case -1:
/* disconnected */
client_destroy(client, "Disconnected");
return FALSE;
case 0:
/* nothing new read */
return TRUE;
default:
/* something was read */
return TRUE;
}
}
void client_input(struct client *client)
{
client->v.input(client);
}