client-common.c revision 2454dfa32c93c20a8522c6ed42fe057baaac9f9a
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "login-common.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "array.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "hostpid.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "llist.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "istream.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "ostream.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "iostream-rawlog.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "process-title.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "buffer.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "str.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "base64.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "str-sanitize.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "safe-memset.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "var-expand.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "master-interface.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "master-service.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "master-service-ssl-settings.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "master-auth.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "auth-client.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "dsasl-client.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "login-proxy.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "ssl-proxy.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "client-common.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstruct client *clients = NULL;
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarzstatic struct client *last_client = NULL;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic unsigned int clients_count = 0;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarzstatic void empty_login_client_allocated_hook(struct client *client ATTR_UNUSED)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz{
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz}
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic login_client_allocated_func_t *hook_client_allocated =
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz empty_login_client_allocated_hook;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzlogin_client_allocated_func_t *
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarzlogin_client_allocated_hook_set(login_client_allocated_func_t *new_hook)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz{
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz login_client_allocated_func_t *old_hook = hook_client_allocated;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz hook_client_allocated = new_hook;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return old_hook;
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz}
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic void client_idle_disconnect_timeout(struct client *client)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz const char *user_reason, *destroy_reason;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz unsigned int secs;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz if (client->master_tag != 0) {
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz secs = ioloop_time - client->auth_finished;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz user_reason = "Timeout while finishing login.";
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz destroy_reason = t_strdup_printf(
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Timeout while finishing login (waited %u secs)", secs);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_log_err(client, destroy_reason);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz } else if (client->auth_request != NULL) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz user_reason =
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Disconnected for inactivity during authentication.";
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz destroy_reason =
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz "Disconnected: Inactivity during authentication";
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz } else if (client->login_proxy != NULL) {
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz secs = ioloop_time - client->created;
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz user_reason = "Timeout while finishing login.";
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz destroy_reason = t_strdup_printf(
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz "proxy: Logging in to %s:%u timed out "
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz "(state=%u, duration=%us)",
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz login_proxy_get_host(client->login_proxy),
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz login_proxy_get_port(client->login_proxy),
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz client->proxy_state, secs);
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz client_log_err(client, destroy_reason);
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz } else {
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz user_reason = "Disconnected for inactivity.";
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz destroy_reason = "Disconnected: Inactivity";
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz }
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason);
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz client_destroy(client, destroy_reason);
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz}
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarzstatic void client_open_streams(struct client *client)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz{
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz client->input = i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->output = o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz o_stream_set_no_error_handling(client->output, TRUE);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (login_rawlog_dir != NULL) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (iostream_rawlog_create(login_rawlog_dir, &client->input,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz &client->output) < 0)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz login_rawlog_dir = NULL;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz}
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarzstatic bool client_is_trusted(struct client *client)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz{
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz const char *const *net;
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz struct ip_addr net_ip;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz unsigned int bits;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->set->login_trusted_networks == NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return FALSE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz for (; *net != NULL; net++) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (net_parse_range(*net, &net_ip, &bits) < 0) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_error("login_trusted_networks: "
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Invalid network '%s'", *net);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz break;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (net_is_in_network(&client->ip, &net_ip, bits))
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return TRUE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return FALSE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz}
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstruct client *
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzclient_create(int fd, bool ssl, pool_t pool,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz const struct master_service_connection *conn,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz const struct login_settings *set,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz const struct master_service_ssl_settings *ssl_set,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz void **other_sets)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct client *client;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(fd != -1);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client = login_binary->client_vfuncs->alloc(pool);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->v = *login_binary->client_vfuncs;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->v.auth_send_challenge == NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->v.auth_send_challenge = client_auth_send_challenge;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->v.auth_parse_response == NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->v.auth_parse_response = client_auth_parse_response;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->created = ioloop_time;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->refcount = 1;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->pool = pool;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->set = set;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->ssl_set = ssl_set;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz p_array_init(&client->module_contexts, client->pool, 5);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->fd = fd;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->tls = ssl;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->local_ip = conn->local_ip;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->local_port = conn->local_port;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->ip = conn->remote_ip;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->remote_port = conn->remote_port;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->real_local_ip = conn->real_local_ip;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->real_local_port = conn->real_local_port;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->real_remote_ip = conn->real_remote_ip;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->real_remote_port = conn->real_remote_port;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->listener_name = p_strdup(client->pool, conn->name);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->trusted = client_is_trusted(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->secured = ssl || client->trusted ||
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz net_ip_compare(&conn->real_remote_ip, &conn->real_local_ip);
4ee5a85e75d520497bd43dbfcc6fc273f3e57ceaMichael Slusarz client->proxy_ttl = LOGIN_PROXY_TTL;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz if (last_client == NULL)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz last_client = client;
4ee5a85e75d520497bd43dbfcc6fc273f3e57ceaMichael Slusarz DLLIST_PREPEND(&clients, client);
4ee5a85e75d520497bd43dbfcc6fc273f3e57ceaMichael Slusarz clients_count++;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
4ee5a85e75d520497bd43dbfcc6fc273f3e57ceaMichael Slusarz client->to_disconnect =
4ee5a85e75d520497bd43dbfcc6fc273f3e57ceaMichael Slusarz timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_idle_disconnect_timeout, client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_open_streams(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz hook_client_allocated(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->v.create(client, other_sets);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (auth_client_is_connected(auth_client))
4ee5a85e75d520497bd43dbfcc6fc273f3e57ceaMichael Slusarz client_notify_auth_ready(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz else
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_set_auth_waiting(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz login_refresh_proctitle();
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz return client;
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz}
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarzvoid client_destroy(struct client *client, const char *reason)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->destroyed)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->destroyed = TRUE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (!client->login_success && reason != NULL) {
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz reason = t_strconcat(reason, " ",
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz client_get_extra_disconnect_reason(client), NULL);
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz }
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz if (reason != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_log(client, reason);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (last_client == client)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz last_client = client->prev;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz DLLIST_REMOVE(&clients, client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz if (client->output != NULL)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz o_stream_uncork(client->output);
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz if (!client->login_success && client->ssl_proxy != NULL)
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz ssl_proxy_destroy(client->ssl_proxy);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->input != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_stream_close(client->input);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->output != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz o_stream_close(client->output);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->master_tag != 0) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->auth_request == NULL);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->authenticating);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->refcount > 1);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->authenticating = FALSE;
f6fb60c7dcfc88895c8c45514c3fc424d3126336Michael Slusarz master_auth_request_abort(master_auth, client->master_tag);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->refcount--;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz } else if (client->auth_request != NULL) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->authenticating);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz sasl_server_auth_abort(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz } else {
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz i_assert(!client->authenticating);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->io != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz io_remove(&client->io);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->to_disconnect != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz timeout_remove(&client->to_disconnect);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->to_auth_waiting != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz timeout_remove(&client->to_auth_waiting);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->auth_response != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz str_free(&client->auth_response);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->fd != -1) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz net_disconnect(client->fd);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->fd = -1;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->proxy_password != NULL) {
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz safe_memset(client->proxy_password, 0,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz strlen(client->proxy_password));
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free_and_null(client->proxy_password);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz if (client->proxy_sasl_client != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz dsasl_client_free(&client->proxy_sasl_client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->login_proxy != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz login_proxy_free(&client->login_proxy);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->v.destroy != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client->v.destroy(client);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client_unref(&client) && initial_service_count == 1) {
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz /* as soon as this connection is done with proxying
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz (or whatever), the process will die. there's no need for
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz authentication anymore, so close the connection.
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz do this only with initial service_count=1, in case there
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz are other clients with pending authentications */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz auth_client_disconnect(auth_client, "unnecessary connection");
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz }
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz login_client_destroyed();
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz login_refresh_proctitle();
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz}
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarzvoid client_destroy_success(struct client *client, const char *reason)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz client->login_success = TRUE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_destroy(client, reason);
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz}
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarzvoid client_destroy_internal_failure(struct client *client)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Internal login failure. "
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Refer to server log for more information.");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_destroy(client, t_strdup_printf(
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz "Internal login failure (pid=%s id=%u)",
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz my_pid, client->master_auth_id));
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz}
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzvoid client_ref(struct client *client)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz client->refcount++;
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz}
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzbool client_unref(struct client **_client)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct client *client = *_client;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->refcount > 0);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (--client->refcount > 0)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return TRUE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz *_client = NULL;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->destroyed);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(client->login_proxy == NULL);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->ssl_proxy != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz ssl_proxy_free(&client->ssl_proxy);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->input != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_stream_unref(&client->input);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->output != NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz o_stream_unref(&client->output);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->proxy_user);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->proxy_master_user);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->virtual_user);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->virtual_user_orig);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->virtual_auth_user);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->auth_mech_name);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_free(client->master_data_prefix);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz pool_unref(&client->pool);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_assert(clients_count > 0);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz clients_count--;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz master_service_client_connection_destroyed(master_service);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz login_refresh_proctitle();
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return FALSE;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz}
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzvoid client_destroy_oldest(void)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz{
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct client *client;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (last_client == NULL) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz /* we have no clients */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz return;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz /* destroy the last client that hasn't successfully authenticated yet.
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz this is usually the last client, but don't kill it if it's just
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz waiting for master to finish its job. */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz for (client = last_client; client != NULL; client = client->prev) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client->master_tag == 0)
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz break;
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if (client == NULL)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client = last_client;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Connection queue full");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_destroy(client, "Disconnected: Connection queue full");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz}
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzvoid clients_destroy_all_reason(const char *reason)
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz{
e7e9ca33af09b6ab77633bcafe27d751adf09c93Michael Slusarz struct client *client, *next;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz for (client = clients; client != NULL; client = next) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz next = client->next;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_notify_disconnect(client,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, reason);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz client_destroy(client, reason);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz }
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz}
void clients_destroy_all(void)
{
clients_destroy_all_reason("Disconnected: Shutting down");
}
static void client_start_tls(struct client *client)
{
int fd_ssl;
client_ref(client);
if (!client_unref(&client) || client->destroyed)
return;
fd_ssl = ssl_proxy_alloc(client->fd, &client->ip, client->pool,
client->set, client->ssl_set,
&client->ssl_proxy);
if (fd_ssl == -1) {
client_notify_disconnect(client,
CLIENT_DISCONNECT_INTERNAL_ERROR,
"TLS initialization failed.");
client_destroy(client,
"Disconnected: TLS initialization failed.");
return;
}
ssl_proxy_set_client(client->ssl_proxy, client);
ssl_proxy_start(client->ssl_proxy);
client->starttls = TRUE;
client->tls = TRUE;
client->secured = TRUE;
login_refresh_proctitle();
client->fd = fd_ssl;
client->io = io_add(client->fd, IO_READ, client_input, client);
i_stream_unref(&client->input);
o_stream_unref(&client->output);
client_open_streams(client);
client->v.starttls(client);
}
static int client_output_starttls(struct client *client)
{
int ret;
if ((ret = o_stream_flush(client->output)) < 0) {
client_destroy(client, "Disconnected");
return 1;
}
if (ret > 0) {
o_stream_unset_flush_callback(client->output);
client_start_tls(client);
}
return 1;
}
void client_cmd_starttls(struct client *client)
{
if (client->tls) {
client->v.notify_starttls(client, FALSE, "TLS is already active.");
return;
}
if (!client_is_tls_enabled(client)) {
client->v.notify_starttls(client, FALSE, "TLS support isn't enabled.");
return;
}
/* remove input handler, SSL proxy gives us a new fd. we also have to
remove it in case we have to wait for buffer to be flushed */
if (client->io != NULL)
io_remove(&client->io);
client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now.");
/* uncork the old fd */
o_stream_uncork(client->output);
if (o_stream_flush(client->output) <= 0) {
/* the buffer has to be flushed */
o_stream_set_flush_pending(client->output, TRUE);
o_stream_set_flush_callback(client->output,
client_output_starttls, client);
} else {
client_start_tls(client);
}
}
unsigned int clients_get_count(void)
{
return clients_count;
}
const char *client_get_session_id(struct client *client)
{
buffer_t *buf, *base64_buf;
struct timeval tv;
uint64_t timestamp;
unsigned int i;
if (client->session_id != NULL)
return client->session_id;
buf = buffer_create_dynamic(pool_datastack_create(), 24);
base64_buf = buffer_create_dynamic(pool_datastack_create(), 24*2);
if (gettimeofday(&tv, NULL) < 0)
i_fatal("gettimeofday(): %m");
timestamp = tv.tv_usec + (long long)tv.tv_sec * 1000ULL*1000ULL;
/* add lowest 48 bits of the timestamp. this gives us a bit less than
9 years until it wraps */
for (i = 0; i < 48; i += 8)
buffer_append_c(buf, (timestamp >> i) & 0xff);
buffer_append_c(buf, client->remote_port & 0xff);
buffer_append_c(buf, (client->remote_port >> 8) & 0xff);
if (IPADDR_IS_V6(&client->ip))
buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6));
else
buffer_append(buf, &client->ip.u.ip4, sizeof(client->ip.u.ip4));
base64_encode(buf->data, buf->used, base64_buf);
client->session_id = p_strdup(client->pool, str_c(base64_buf));
return client->session_id;
}
static struct var_expand_table login_var_expand_empty_tab[] = {
{ 'u', NULL, "user" },
{ 'n', NULL, "username" },
{ 'd', NULL, "domain" },
{ 's', NULL, "service" },
{ 'h', NULL, "home" },
{ 'l', NULL, "lip" },
{ 'r', NULL, "rip" },
{ 'p', NULL, "pid" },
{ 'm', NULL, "mech" },
{ 'a', NULL, "lport" },
{ 'b', NULL, "rport" },
{ 'c', NULL, "secured" },
{ 'k', NULL, "ssl_security" },
{ 'e', NULL, "mail_pid" },
{ '\0', NULL, "session" },
{ '\0', NULL, "real_lip" },
{ '\0', NULL, "real_rip" },
{ '\0', NULL, "real_lport" },
{ '\0', NULL, "real_rport" },
{ '\0', NULL, "orig_user" },
{ '\0', NULL, "orig_username" },
{ '\0', NULL, "orig_domain" },
{ '\0', NULL, "auth_user" },
{ '\0', NULL, "auth_username" },
{ '\0', NULL, "auth_domain" },
{ '\0', NULL, "listener" },
{ '\0', NULL, "local_name" },
{ '\0', NULL, NULL }
};
static void
get_var_expand_users(struct var_expand_table *tab, const char *user)
{
unsigned int i;
tab[0].value = user;
tab[1].value = t_strcut(user, '@');
tab[2].value = i_strchr_to_next(user, '@');
for (i = 0; i < 3; i++)
tab[i].value = str_sanitize(tab[i].value, 80);
}
static const struct var_expand_table *
get_var_expand_table(struct client *client)
{
struct var_expand_table *tab;
tab = t_malloc_no0(sizeof(login_var_expand_empty_tab));
memcpy(tab, login_var_expand_empty_tab,
sizeof(login_var_expand_empty_tab));
if (client->virtual_user != NULL)
get_var_expand_users(tab, client->virtual_user);
tab[3].value = login_binary->protocol;
tab[4].value = getenv("HOME");
tab[5].value = net_ip2addr(&client->local_ip);
tab[6].value = net_ip2addr(&client->ip);
tab[7].value = my_pid;
tab[8].value = client->auth_mech_name == NULL ? NULL :
str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
tab[9].value = dec2str(client->local_port);
tab[10].value = dec2str(client->remote_port);
if (!client->tls) {
tab[11].value = client->secured ? "secured" : NULL;
tab[12].value = "";
} else {
const char *ssl_state =
ssl_proxy_is_handshaked(client->ssl_proxy) ?
"TLS" : "TLS handshaking";
const char *ssl_error =
ssl_proxy_get_last_error(client->ssl_proxy);
tab[11].value = ssl_error == NULL ? ssl_state :
t_strdup_printf("%s: %s", ssl_state, ssl_error);
tab[12].value =
ssl_proxy_get_security_string(client->ssl_proxy);
}
tab[13].value = client->mail_pid == 0 ? "" :
dec2str(client->mail_pid);
tab[14].value = client_get_session_id(client);
tab[15].value = net_ip2addr(&client->real_local_ip);
tab[16].value = net_ip2addr(&client->real_remote_ip);
tab[17].value = dec2str(client->real_local_port);
tab[18].value = dec2str(client->real_remote_port);
if (client->virtual_user_orig != NULL)
get_var_expand_users(tab+19, client->virtual_user_orig);
else {
tab[19].value = tab[0].value;
tab[20].value = tab[1].value;
tab[21].value = tab[2].value;
}
if (client->virtual_auth_user != NULL)
get_var_expand_users(tab+22, client->virtual_auth_user);
else {
tab[22].value = tab[19].value;
tab[23].value = tab[20].value;
tab[24].value = tab[21].value;
}
tab[25].value = client->listener_name;
tab[26].value = str_sanitize(client->local_name, 256);
return tab;
}
static bool have_username_key(const char *str)
{
char key;
for (; *str != '\0'; str++) {
if (str[0] == '%' && str[1] != '\0') {
str++;
key = var_get_key(str);
if (key == 'u' || key == 'n')
return TRUE;
}
}
return FALSE;
}
static int
client_var_expand_func_passdb(const char *data, void *context,
const char **value_r,
const char **error_r ATTR_UNUSED)
{
struct client *client = context;
const char *field_name = data;
unsigned int i;
size_t field_name_len;
*value_r = NULL;
if (client->auth_passdb_args == NULL)
return 1;
field_name_len = strlen(field_name);
for (i = 0; client->auth_passdb_args[i] != NULL; i++) {
if (strncmp(client->auth_passdb_args[i], field_name,
field_name_len) == 0 &&
client->auth_passdb_args[i][field_name_len] == '=') {
*value_r = client->auth_passdb_args[i] + field_name_len+1;
return 1;
}
}
return 1;
}
static const char *
client_get_log_str(struct client *client, const char *msg)
{
static const struct var_expand_func_table func_table[] = {
{ "passdb", client_var_expand_func_passdb },
{ NULL, NULL }
};
static bool expand_error_logged = FALSE;
const struct var_expand_table *var_expand_table;
char *const *e;
const char *error;
string_t *str, *str2;
unsigned int pos;
var_expand_table = get_var_expand_table(client);
str = t_str_new(256);
str2 = t_str_new(128);
for (e = client->set->log_format_elements_split; *e != NULL; e++) {
pos = str_len(str);
if (var_expand_with_funcs(str, *e, var_expand_table,
func_table, client, &error) <= 0 &&
!expand_error_logged) {
i_error("Failed to expand log_format_elements=%s: %s",
*e, error);
expand_error_logged = TRUE;
}
if (have_username_key(*e)) {
/* username is added even if it's empty */
} else {
str_truncate(str2, 0);
if (var_expand(str2, *e, login_var_expand_empty_tab,
&error) <= 0) {
/* we just logged this error above. no need
to do it again. */
}
if (strcmp(str_c(str)+pos, str_c(str2)) == 0) {
/* empty %variables, don't add */
str_truncate(str, pos);
continue;
}
}
if (str_len(str) > 0)
str_append(str, ", ");
}
if (str_len(str) > 0)
str_truncate(str, str_len(str)-2);
const struct var_expand_table tab[3] = {
{ 's', t_strdup(str_c(str)), NULL },
{ '$', msg, NULL },
{ '\0', NULL, NULL }
};
str_truncate(str, 0);
if (var_expand(str, client->set->login_log_format, tab, &error) <= 0) {
i_error("Failed to expand login_log_format=%s: %s",
client->set->login_log_format, error);
expand_error_logged = TRUE;
}
return str_c(str);
}
void client_log(struct client *client, const char *msg)
{
T_BEGIN {
i_info("%s", client_get_log_str(client, msg));
} T_END;
}
void client_log_err(struct client *client, const char *msg)
{
T_BEGIN {
i_error("%s", client_get_log_str(client, msg));
} T_END;
}
void client_log_warn(struct client *client, const char *msg)
{
T_BEGIN {
i_warning("%s", client_get_log_str(client, msg));
} T_END;
}
bool client_is_tls_enabled(struct client *client)
{
return ssl_initialized && strcmp(client->ssl_set->ssl, "no") != 0;
}
const char *client_get_extra_disconnect_reason(struct client *client)
{
unsigned int auth_secs = client->auth_first_started == 0 ? 0 :
ioloop_time - client->auth_first_started;
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy != NULL) {
if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
return "(client sent an invalid cert)";
if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
return "(client didn't send a cert)";
}
if (!client->notified_auth_ready)
return t_strdup_printf(
"(disconnected before auth was ready, waited %u secs)",
(unsigned int)(ioloop_time - client->created));
if (client->auth_attempts == 0) {
return t_strdup_printf("(no auth attempts in %u secs)",
(unsigned int)(ioloop_time - client->created));
}
/* some auth attempts without SSL/TLS */
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy == NULL)
return "(cert required, client didn't start TLS)";
if (client->auth_waiting && client->auth_attempts == 1) {
return t_strdup_printf("(client didn't finish SASL auth, "
"waited %u secs)", auth_secs);
}
if (client->auth_request != NULL && client->auth_attempts == 1) {
return t_strdup_printf("(disconnected while authenticating, "
"waited %u secs)", auth_secs);
}
if (client->authenticating && client->auth_attempts == 1) {
return t_strdup_printf("(disconnected while finishing login, "
"waited %u secs)", auth_secs);
}
if (client->auth_try_aborted && client->auth_attempts == 1)
return "(aborted authentication)";
if (client->auth_process_comm_fail)
return "(auth process communication failure)";
if (client->proxy_auth_failed)
return "(proxy dest auth failed)";
if (client->auth_successes > 0) {
return t_strdup_printf("(internal failure, %u successful auths)",
client->auth_successes);
}
switch (client->last_auth_fail) {
case CLIENT_AUTH_FAIL_CODE_AUTHZFAILED:
return t_strdup_printf(
"(authorization failed, %u attempts in %u secs)",
client->auth_attempts, auth_secs);
case CLIENT_AUTH_FAIL_CODE_TEMPFAIL:
return "(auth service reported temporary failure)";
case CLIENT_AUTH_FAIL_CODE_USER_DISABLED:
return "(user disabled)";
case CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED:
return "(password expired)";
case CLIENT_AUTH_FAIL_CODE_INVALID_BASE64:
return "(sent invalid base64 in response)";
case CLIENT_AUTH_FAIL_CODE_LOGIN_DISABLED:
return "(login disabled)";
case CLIENT_AUTH_FAIL_CODE_MECH_INVALID:
return "(tried to use unsupported auth mechanism)";
case CLIENT_AUTH_FAIL_CODE_MECH_SSL_REQUIRED:
return "(tried to use disallowed plaintext auth)";
default:
break;
}
return t_strdup_printf("(auth failed, %u attempts in %u secs)",
client->auth_attempts, auth_secs);
}
void client_notify_disconnect(struct client *client,
enum client_disconnect_reason reason,
const char *text)
{
if (!client->notified_disconnect) {
if (client->v.notify_disconnect != NULL)
client->v.notify_disconnect(client, reason, text);
client->notified_disconnect = TRUE;
}
}
void client_notify_auth_ready(struct client *client)
{
if (!client->notified_auth_ready) {
if (client->v.notify_auth_ready != NULL)
client->v.notify_auth_ready(client);
client->notified_auth_ready = TRUE;
}
}
void client_notify_status(struct client *client, bool bad, const char *text)
{
if (client->v.notify_status != NULL)
client->v.notify_status(client, bad, text);
}
void client_send_raw_data(struct client *client, const void *data, size_t size)
{
ssize_t ret;
ret = o_stream_send(client->output, data, size);
if (ret < 0 || (size_t)ret != size) {
/* either disconnection or buffer full. in either case we want
this connection destroyed. however destroying it here might
break things if client is still tried to be accessed without
being referenced.. */
i_stream_close(client->input);
}
}
void client_send_raw(struct client *client, const char *data)
{
client_send_raw_data(client, data, strlen(data));
}
bool client_read(struct client *client)
{
switch (i_stream_read(client->input)) {
case -2:
/* buffer full */
client_notify_disconnect(client,
CLIENT_DISCONNECT_RESOURCE_CONSTRAINT,
"Input buffer full, aborting");
client_destroy(client, "Disconnected: Input buffer full");
return FALSE;
case -1:
/* disconnected */
client_destroy(client, "Disconnected");
return FALSE;
case 0:
/* nothing new read */
return TRUE;
default:
/* something was read */
return TRUE;
}
}
void client_input(struct client *client)
{
client->v.input(client);
}