client-common.c revision e59faf65ce864fe95dc00f5d52b8323cdbd0608a
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Boschstruct client *clients = NULL, *last_client = NULL;
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Boschstatic unsigned int clients_count = 0;
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Boschstatic void client_idle_disconnect_timeout(struct client *client)
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BYE,
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch "Disconnected for inactivity.");
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch client_destroy(client, "Disconnected: Inactivity");
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Boschstatic void client_open_streams(struct client *client)
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch const struct login_settings *set, void **other_sets,
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch client->v.auth_send_challenge = client_auth_send_challenge;
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch client->v.auth_parse_response = client_auth_parse_response;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschvoid client_destroy(struct client *client, const char *reason)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (!client->login_success && reason != NULL) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch client_get_extra_disconnect_reason(client), NULL);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch master_auth_request_abort(master_auth, client->master_tag);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch master_service_get_service_count(master_service) == 1) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* as soon as this connection is done with proxying
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch (or whatever), the process will die. there's no need for
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch authentication anymore, so close the connection. */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschvoid client_destroy_success(struct client *client, const char *reason)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschvoid client_destroy_internal_failure(struct client *client)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Internal login failure. "
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch "Refer to server log for more information.");
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch client_destroy(client, "Internal login failure");
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch master_service_client_connection_destroyed(master_service);
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch /* we have no clients */
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch /* destroy the last client that hasn't successfully authenticated yet.
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch this is usually the last client, but don't kill it if it's just
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch waiting for master to finish its job. */
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch for (client = last_client; client != NULL; client = client->prev) {
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch client_destroy(client, "Disconnected: Connection queue full");
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (client = clients; client != NULL; client = next) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch client_destroy(client, "Disconnected: Shutting down");
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Boschstatic void client_start_tls(struct client *client)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (!client_unref(&client) || client->destroyed)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch fd_ssl = ssl_proxy_alloc(client->fd, &client->ip,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BYE,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "TLS initialization failed.");
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Disconnected: TLS initialization failed.");
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch ssl_proxy_set_client(client->ssl_proxy, client);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch client->io = io_add(client->fd, IO_READ, client_input, client);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic int client_output_starttls(struct client *client)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = o_stream_flush(client->output)) < 0) {
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch o_stream_unset_flush_callback(client->output);
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Boschvoid client_cmd_starttls(struct client *client)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BAD,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "TLS is already active.");
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BAD,
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch "TLS support isn't enabled.");
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* remove input handler, SSL proxy gives us a new fd. we also have to
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch remove it in case we have to wait for buffer to be flushed */
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch "Begin TLS negotiation now.");
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch /* uncork the old fd */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* the buffer has to be flushed */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch o_stream_set_flush_pending(client->output, TRUE);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschunsigned int clients_get_count(void)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic const struct var_expand_table *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch static struct var_expand_table static_tab[] = {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned int i;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[1].value = t_strcut(client->virtual_user, '@');
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[2].value = strchr(client->virtual_user, '@');
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (i = 0; i < 3; i++)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[i].value = str_sanitize(tab[i].value, 80);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[5].value = net_ip2addr(&client->local_ip);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[8].value = client->auth_mech_name == NULL ? NULL :
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[11].value = client->secured ? "secured" : NULL;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tab[11].value = ssl_error == NULL ? ssl_state :
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch t_strdup_printf("%s: %s", ssl_state, ssl_error);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ssl_proxy_get_security_string(client->ssl_proxy);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic bool have_key(const struct var_expand_table *table, const char *str)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned int i;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic const char *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschclient_get_log_str(struct client *client, const char *msg)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch static struct var_expand_table static_tab[3] = {
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const struct var_expand_table *var_expand_table;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const char *p;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch char *const *e;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch var_expand_table = get_var_expand_table(client);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch for (e = client->set->log_format_elements_split; *e != NULL; e++) {
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch for (p = *e; *p != '\0'; p++) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch var_expand(str, client->set->login_log_format, tab);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Boschvoid client_log(struct client *client, const char *msg)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch i_info("%s", client_get_log_str(client, msg));
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Boschvoid client_log_err(struct client *client, const char *msg)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch i_error("%s", client_get_log_str(client, msg));
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch const char *const *net;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch unsigned int bits;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (client->set->login_trusted_networks == NULL)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (net_parse_range(*net, &net_ip, &bits) < 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (net_is_in_network(&client->ip, &net_ip, bits))
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Boschconst char *client_get_extra_disconnect_reason(struct client *client)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (client->set->ssl_require_client_cert && client->ssl_proxy != NULL) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(client sent an invalid cert)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(client didn't send a cert)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(no auth attempts)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* some auth attempts without SSL/TLS */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(tried to use disabled plaintext auth)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(cert required, client didn't start TLS)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(tried to use unsupported auth mechanism)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (client->auth_request != NULL && client->auth_attempts == 1)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(disconnected while authenticating)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (client->auth_try_aborted && client->auth_attempts == 1)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return "(aborted authentication)";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return t_strdup_printf("(auth failed, %u attempts)",
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Boschvoid client_send_line(struct client *client, enum client_cmd_reply reply,
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Boschvoid client_send_raw_data(struct client *client, const void *data, size_t size)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch ret = o_stream_send(client->output, data, size);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* either disconnection or buffer full. in either case we want
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch this connection destroyed. however destroying it here might
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch break things if client is still tried to be accessed without
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch being referenced.. */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Boschvoid client_send_raw(struct client *client, const char *data)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch client_send_raw_data(client, data, strlen(data));
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* buffer full */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch client_send_line(client, CLIENT_CMD_REPLY_BYE,
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch "Input buffer full, aborting");
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch client_destroy(client, "Disconnected: Input buffer full");
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* disconnected */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* nothing new read */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* something was read */