client-common.c revision ae797f3368ebb86e7786ca25d7c9c703f672b9f5
10139N/A/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
10139N/A
10139N/A#include "login-common.h"
12219N/A#include "array.h"
10139N/A#include "hostpid.h"
10139N/A#include "llist.h"
10139N/A#include "istream.h"
10139N/A#include "ostream.h"
10139N/A#include "iostream-rawlog.h"
10139N/A#include "process-title.h"
10139N/A#include "hook-build.h"
15291N/A#include "buffer.h"
10139N/A#include "str.h"
10615N/A#include "strescape.h"
15301N/A#include "base64.h"
15759N/A#include "str-sanitize.h"
10139N/A#include "safe-memset.h"
10139N/A#include "var-expand.h"
10139N/A#include "master-interface.h"
10139N/A#include "master-service.h"
15301N/A#include "master-service-ssl-settings.h"
10794N/A#include "master-auth.h"
10139N/A#include "auth-client.h"
10794N/A#include "dsasl-client.h"
12773N/A#include "login-proxy.h"
12773N/A#include "ssl-proxy.h"
12773N/A#include "client-common.h"
10794N/A
10139N/A
10640N/Astruct client *clients = NULL;
10139N/Astatic struct client *last_client = NULL;
10640N/Astatic unsigned int clients_count = 0;
10139N/A
10820N/Astruct login_client_module_hooks {
10139N/A struct module *module;
10820N/A const struct login_client_hooks *hooks;
10339N/A};
10820N/A
10861N/Astatic ARRAY(struct login_client_module_hooks) module_hooks = ARRAY_INIT;
13306N/A
12139N/Avoid login_client_hooks_add(struct module *module,
12139N/A const struct login_client_hooks *hooks)
13379N/A{
13379N/A struct login_client_module_hooks *hook;
15219N/A
14420N/A hook = array_append_space(&module_hooks);
14503N/A hook->module = module;
14503N/A hook->hooks = hooks;
10139N/A}
10139N/A
10139N/Avoid login_client_hooks_remove(const struct login_client_hooks *hooks)
10139N/A{
10139N/A const struct login_client_module_hooks *module_hook;
10139N/A unsigned int idx = UINT_MAX;
10139N/A
10139N/A array_foreach(&module_hooks, module_hook) {
10139N/A if (module_hook->hooks == hooks) {
10139N/A idx = array_foreach_idx(&module_hooks, module_hook);
10139N/A break;
10139N/A }
10139N/A }
10139N/A i_assert(idx != UINT_MAX);
10139N/A
10139N/A array_delete(&module_hooks, idx, 1);
10139N/A}
10139N/A
10139N/Astatic void hook_login_client_allocated(struct client *client)
10139N/A{
10139N/A const struct login_client_module_hooks *module_hook;
10139N/A struct hook_build_context *ctx;
10139N/A
10139N/A ctx = hook_build_init((void *)&client->v, sizeof(client->v));
10139N/A client->vlast = &client->v;
10139N/A array_foreach(&module_hooks, module_hook) {
10139N/A if (module_hook->hooks->client_allocated != NULL) T_BEGIN {
10139N/A module_hook->hooks->client_allocated(client);
10139N/A hook_build_update(ctx, client->vlast);
10139N/A } T_END;
10139N/A }
10139N/A client->vlast = NULL;
10139N/A hook_build_deinit(&ctx);
10139N/A}
10139N/A
10139N/Astatic void client_idle_disconnect_timeout(struct client *client)
10139N/A{
10139N/A const char *user_reason, *destroy_reason;
10139N/A unsigned int secs;
10139N/A
10139N/A if (client->master_tag != 0) {
10139N/A secs = ioloop_time - client->auth_finished;
10139N/A user_reason = "Timeout while finishing login.";
10139N/A destroy_reason = t_strdup_printf(
10139N/A "Timeout while finishing login (waited %u secs)", secs);
10139N/A client_log_err(client, destroy_reason);
10139N/A } else if (client->auth_request != NULL) {
10139N/A user_reason =
10139N/A "Disconnected for inactivity during authentication.";
10139N/A destroy_reason =
10139N/A "Disconnected: Inactivity during authentication";
10139N/A } else if (client->login_proxy != NULL) {
10139N/A secs = ioloop_time - client->created;
10139N/A user_reason = "Timeout while finishing login.";
10139N/A destroy_reason = t_strdup_printf(
10139N/A "proxy: Logging in to %s:%u timed out "
10139N/A "(state=%s, duration=%us)",
10139N/A login_proxy_get_host(client->login_proxy),
10139N/A login_proxy_get_port(client->login_proxy),
10139N/A client_proxy_get_state(client), secs);
10139N/A client_log_err(client, destroy_reason);
10139N/A } else {
10139N/A user_reason = "Disconnected for inactivity.";
10139N/A destroy_reason = "Disconnected: Inactivity";
10139N/A }
10139N/A client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason);
10139N/A client_destroy(client, destroy_reason);
10794N/A}
12754N/A
10139N/Astatic void client_open_streams(struct client *client)
10139N/A{
10139N/A client->input = i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE);
11087N/A client->output = o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE);
11087N/A o_stream_set_no_error_handling(client->output, TRUE);
11087N/A
15700N/A if (login_rawlog_dir != NULL) {
15700N/A if (iostream_rawlog_create(login_rawlog_dir, &client->input,
13306N/A &client->output) < 0)
12139N/A login_rawlog_dir = NULL;
13379N/A }
15449N/A}
14503N/A
10861N/Astatic bool client_is_trusted(struct client *client)
10139N/A{
10139N/A const char *const *net;
10139N/A struct ip_addr net_ip;
10139N/A unsigned int bits;
10139N/A
10139N/A if (client->set->login_trusted_networks == NULL)
10139N/A return FALSE;
10139N/A
10139N/A net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
10139N/A for (; *net != NULL; net++) {
10139N/A if (net_parse_range(*net, &net_ip, &bits) < 0) {
10139N/A i_error("login_trusted_networks: "
10139N/A "Invalid network '%s'", *net);
10139N/A break;
10139N/A }
10139N/A
10139N/A if (net_is_in_network(&client->ip, &net_ip, bits))
10139N/A return TRUE;
11344N/A }
11344N/A return FALSE;
11344N/A}
11344N/A
11344N/Astruct client *
10139N/Aclient_create(int fd, bool ssl, pool_t pool,
10139N/A const struct master_service_connection *conn,
10139N/A const struct login_settings *set,
10139N/A const struct master_service_ssl_settings *ssl_set,
11994N/A void **other_sets)
10139N/A{
11994N/A struct client *client;
12773N/A
12773N/A i_assert(fd != -1);
12773N/A
12773N/A client = login_binary->client_vfuncs->alloc(pool);
12773N/A client->v = *login_binary->client_vfuncs;
11994N/A if (client->v.auth_send_challenge == NULL)
10139N/A client->v.auth_send_challenge = client_auth_send_challenge;
10139N/A if (client->v.auth_parse_response == NULL)
10139N/A client->v.auth_parse_response = client_auth_parse_response;
10139N/A
10139N/A client->created = ioloop_time;
10139N/A client->refcount = 1;
10139N/A
10139N/A client->pool = pool;
10139N/A client->preproxy_pool = pool_alloconly_create(MEMPOOL_GROWING"preproxy pool", 256);
10139N/A client->set = set;
10139N/A client->ssl_set = ssl_set;
10139N/A p_array_init(&client->module_contexts, client->pool, 5);
10139N/A
10139N/A client->fd = fd;
10139N/A client->tls = ssl;
10139N/A
10139N/A client->local_ip = conn->local_ip;
10139N/A client->local_port = conn->local_port;
11826N/A client->ip = conn->remote_ip;
15528N/A client->remote_port = conn->remote_port;
11344N/A client->real_local_ip = conn->real_local_ip;
11344N/A client->real_local_port = conn->real_local_port;
10139N/A client->real_remote_ip = conn->real_remote_ip;
10139N/A client->real_remote_port = conn->real_remote_port;
10139N/A client->listener_name = p_strdup(client->pool, conn->name);
10139N/A
10139N/A client->trusted = client_is_trusted(client);
10139N/A client->secured = ssl || client->trusted ||
10139N/A net_ip_compare(&conn->real_remote_ip, &conn->real_local_ip);
10139N/A client->proxy_ttl = LOGIN_PROXY_TTL;
10139N/A
10139N/A if (last_client == NULL)
10139N/A last_client = client;
10139N/A DLLIST_PREPEND(&clients, client);
10139N/A clients_count++;
10139N/A
10139N/A client->to_disconnect =
10139N/A timeout_add(CLIENT_LOGIN_TIMEOUT_MSECS,
10139N/A client_idle_disconnect_timeout, client);
10139N/A client_open_streams(client);
10139N/A
10139N/A hook_login_client_allocated(client);
10139N/A client->v.create(client, other_sets);
10139N/A
10139N/A if (auth_client_is_connected(auth_client))
10139N/A client_notify_auth_ready(client);
10139N/A else
10139N/A client_set_auth_waiting(client);
10139N/A
10139N/A login_refresh_proctitle();
10139N/A return client;
10139N/A}
10139N/A
10139N/Avoid client_destroy(struct client *client, const char *reason)
10139N/A{
10139N/A if (client->destroyed)
10139N/A return;
10139N/A client->destroyed = TRUE;
10139N/A
10139N/A if (client->preproxy_pool != NULL)
10139N/A pool_unref(&client->preproxy_pool);
12402N/A
10139N/A if (!client->login_success && reason != NULL) {
10139N/A const char *extra_reason =
10139N/A client_get_extra_disconnect_reason(client);
10139N/A if (extra_reason[0] != '\0')
10139N/A reason = t_strconcat(reason, " ", extra_reason, NULL);
10139N/A }
10139N/A if (reason != NULL)
10139N/A client_log(client, reason);
10139N/A
10139N/A if (last_client == client)
10139N/A last_client = client->prev;
10139N/A DLLIST_REMOVE(&clients, client);
10139N/A
10139N/A if (client->output != NULL)
10139N/A o_stream_uncork(client->output);
10139N/A if (!client->login_success && client->ssl_proxy != NULL)
10139N/A ssl_proxy_destroy(client->ssl_proxy);
10139N/A if (client->input != NULL)
10139N/A i_stream_close(client->input);
10139N/A if (client->output != NULL)
10139N/A o_stream_close(client->output);
10139N/A
10139N/A if (client->master_tag != 0) {
10139N/A i_assert(client->auth_request == NULL);
10139N/A i_assert(client->authenticating);
10139N/A i_assert(client->refcount > 1);
10139N/A client->authenticating = FALSE;
15759N/A master_auth_request_abort(master_auth, client->master_tag);
15759N/A client->refcount--;
15700N/A } else if (client->auth_request != NULL) {
15700N/A i_assert(client->authenticating);
15693N/A sasl_server_auth_abort(client);
15693N/A } else {
15624N/A i_assert(!client->authenticating);
15624N/A }
15528N/A
15528N/A if (client->io != NULL)
15528N/A io_remove(&client->io);
15528N/A if (client->to_disconnect != NULL)
15528N/A timeout_remove(&client->to_disconnect);
15472N/A if (client->to_auth_waiting != NULL)
15474N/A timeout_remove(&client->to_auth_waiting);
15474N/A if (client->auth_response != NULL)
15472N/A str_free(&client->auth_response);
15472N/A
15449N/A if (client->fd != -1) {
15449N/A net_disconnect(client->fd);
15448N/A client->fd = -1;
15448N/A }
15301N/A
15301N/A if (client->proxy_password != NULL) {
15291N/A safe_memset(client->proxy_password, 0,
15291N/A strlen(client->proxy_password));
15275N/A i_free_and_null(client->proxy_password);
15275N/A }
15219N/A
15219N/A if (client->proxy_sasl_client != NULL)
14503N/A dsasl_client_free(&client->proxy_sasl_client);
14503N/A if (client->login_proxy != NULL)
14464N/A login_proxy_free(&client->login_proxy);
14464N/A if (client->v.destroy != NULL)
14420N/A client->v.destroy(client);
14420N/A if (client_unref(&client) && initial_service_count == 1) {
14420N/A /* as soon as this connection is done with proxying
14185N/A (or whatever), the process will die. there's no need for
14185N/A authentication anymore, so close the connection.
14123N/A do this only with initial service_count=1, in case there
14123N/A are other clients with pending authentications */
13925N/A auth_client_disconnect(auth_client, "unnecessary connection");
13925N/A }
13932N/A login_client_destroyed();
13754N/A login_refresh_proctitle();
13754N/A}
13754N/A
13644N/Avoid client_destroy_success(struct client *client, const char *reason)
13644N/A{
13644N/A client->login_success = TRUE;
13416N/A client_destroy(client, reason);
13416N/A}
13416N/A
13379N/Avoid client_destroy_internal_failure(struct client *client)
13313N/A{
13313N/A client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR,
13300N/A "Internal login failure. "
13300N/A "Refer to server log for more information.");
13300N/A client_destroy(client, t_strdup_printf(
13103N/A "Internal login failure (pid=%s id=%u)",
13103N/A my_pid, client->master_auth_id));
12988N/A}
12988N/A
12972N/Avoid client_ref(struct client *client)
12972N/A{
12571N/A client->refcount++;
12571N/A}
12488N/A
12571N/Abool client_unref(struct client **_client)
12466N/A{
12466N/A struct client *client = *_client;
12413N/A
12413N/A i_assert(client->refcount > 0);
12413N/A if (--client->refcount > 0)
12402N/A return TRUE;
12402N/A
12402N/A *_client = NULL;
12402N/A
12402N/A i_assert(client->destroyed);
12387N/A i_assert(client->login_proxy == NULL);
12387N/A
12370N/A if (client->ssl_proxy != NULL)
12370N/A ssl_proxy_free(&client->ssl_proxy);
12308N/A if (client->input != NULL)
12308N/A i_stream_unref(&client->input);
12308N/A if (client->output != NULL)
12288N/A o_stream_unref(&client->output);
12288N/A
12219N/A i_free(client->proxy_user);
12219N/A i_free(client->proxy_master_user);
12071N/A i_free(client->virtual_user);
12071N/A i_free(client->virtual_user_orig);
11922N/A i_free(client->virtual_auth_user);
11922N/A i_free(client->auth_mech_name);
11828N/A i_free(client->master_data_prefix);
11828N/A pool_unref(&client->pool);
11826N/A
11826N/A i_assert(clients_count > 0);
11819N/A clients_count--;
11819N/A
11416N/A master_service_client_connection_destroyed(master_service);
11416N/A login_refresh_proctitle();
11344N/A return FALSE;
11344N/A}
11253N/A
11253N/Avoid client_destroy_oldest(void)
11183N/A{
11183N/A struct client *client;
11172N/A
11172N/A if (last_client == NULL) {
11120N/A /* we have no clients */
11120N/A return;
11087N/A }
11087N/A
11067N/A /* destroy the last client that hasn't successfully authenticated yet.
11067N/A this is usually the last client, but don't kill it if it's just
10986N/A waiting for master to finish its job. */
10986N/A for (client = last_client; client != NULL; client = client->prev) {
10980N/A if (client->master_tag == 0)
10980N/A break;
10917N/A }
10917N/A if (client == NULL)
10903N/A client = last_client;
10903N/A
10891N/A client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT,
10891N/A "Connection queue full");
10861N/A client_destroy(client, "Disconnected: Connection queue full");
10861N/A}
10861N/A
10820N/Avoid clients_destroy_all_reason(const char *reason)
10820N/A{
10794N/A struct client *client, *next;
10794N/A
10742N/A for (client = clients; client != NULL; client = next) {
10742N/A next = client->next;
10742N/A client_notify_disconnect(client,
10704N/A CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, reason);
10704N/A client_destroy(client, reason);
10640N/A }
10640N/A}
10640N/A
10613N/Avoid clients_destroy_all(void)
10615N/A{
10535N/A clients_destroy_all_reason("Disconnected: Shutting down");
10535N/A}
10476N/A
10476N/Astatic void client_start_tls(struct client *client)
10476N/A{
10436N/A int fd_ssl;
10438N/A
10438N/A client_ref(client);
10438N/A if (!client_unref(&client) || client->destroyed)
10436N/A return;
10436N/A
10418N/A fd_ssl = ssl_proxy_alloc(client->fd, &client->ip, client->pool,
10418N/A client->set, client->ssl_set,
10418N/A &client->ssl_proxy);
10418N/A if (fd_ssl == -1) {
10418N/A client_notify_disconnect(client,
10418N/A CLIENT_DISCONNECT_INTERNAL_ERROR,
10339N/A "TLS initialization failed.");
10339N/A client_destroy(client,
10283N/A "Disconnected: TLS initialization failed.");
10284N/A return;
10169N/A }
10169N/A ssl_proxy_set_client(client->ssl_proxy, client);
10139N/A ssl_proxy_start(client->ssl_proxy);
10139N/A
10139N/A client->starttls = TRUE;
10139N/A client->tls = TRUE;
10139N/A client->secured = TRUE;
10139N/A login_refresh_proctitle();
10139N/A
10139N/A client->fd = fd_ssl;
10139N/A client->io = io_add(client->fd, IO_READ, client_input, client);
10139N/A i_stream_unref(&client->input);
10139N/A o_stream_unref(&client->output);
10139N/A client_open_streams(client);
10139N/A
10139N/A client->v.starttls(client);
10139N/A}
10139N/A
10139N/Astatic int client_output_starttls(struct client *client)
10139N/A{
10139N/A int ret;
10139N/A
10139N/A if ((ret = o_stream_flush(client->output)) < 0) {
10139N/A client_destroy(client, "Disconnected");
10139N/A return 1;
10139N/A }
10139N/A
10139N/A if (ret > 0) {
10139N/A o_stream_unset_flush_callback(client->output);
10139N/A client_start_tls(client);
10139N/A }
10139N/A return 1;
10139N/A}
10139N/A
10139N/Avoid client_cmd_starttls(struct client *client)
10139N/A{
10139N/A if (client->tls) {
10139N/A client->v.notify_starttls(client, FALSE, "TLS is already active.");
10139N/A return;
10139N/A }
10139N/A
10139N/A if (!client_is_tls_enabled(client)) {
10139N/A client->v.notify_starttls(client, FALSE, "TLS support isn't enabled.");
10139N/A return;
10139N/A }
10139N/A
10139N/A /* remove input handler, SSL proxy gives us a new fd. we also have to
10139N/A remove it in case we have to wait for buffer to be flushed */
10535N/A if (client->io != NULL)
10139N/A io_remove(&client->io);
10139N/A
10139N/A client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now.");
10139N/A
10139N/A /* uncork the old fd */
10139N/A o_stream_uncork(client->output);
10139N/A
10139N/A if (o_stream_flush(client->output) <= 0) {
10139N/A /* the buffer has to be flushed */
10139N/A o_stream_set_flush_pending(client->output, TRUE);
10139N/A o_stream_set_flush_callback(client->output,
10139N/A client_output_starttls, client);
10139N/A } else {
10139N/A client_start_tls(client);
10139N/A }
10139N/A}
10139N/A
10139N/Aunsigned int clients_get_count(void)
10139N/A{
10139N/A return clients_count;
10139N/A}
10139N/A
10139N/Avoid client_add_forward_field(struct client *client, const char *key,
10139N/A const char *value)
10139N/A{
10139N/A if (client->forward_fields == NULL)
10139N/A client->forward_fields = str_new(client->preproxy_pool, 32);
10139N/A else
10139N/A str_append_c(client->forward_fields, '\t');
10139N/A /* prefixing is done by auth process */
10139N/A str_append_tabescaped(client->forward_fields, key);
10139N/A str_append_c(client->forward_fields, '=');
10139N/A str_append_tabescaped(client->forward_fields, value);
10139N/A}
10139N/A
10139N/Aconst char *client_get_session_id(struct client *client)
10139N/A{
10139N/A buffer_t *buf, *base64_buf;
10139N/A struct timeval tv;
10139N/A uint64_t timestamp;
10139N/A unsigned int i;
10139N/A
10139N/A if (client->session_id != NULL)
10139N/A return client->session_id;
10139N/A
10139N/A buf = buffer_create_dynamic(pool_datastack_create(), 24);
10139N/A base64_buf = buffer_create_dynamic(pool_datastack_create(), 24*2);
10139N/A
10139N/A if (gettimeofday(&tv, NULL) < 0)
10139N/A i_fatal("gettimeofday(): %m");
10139N/A timestamp = tv.tv_usec + (long long)tv.tv_sec * 1000ULL*1000ULL;
10139N/A
10139N/A /* add lowest 48 bits of the timestamp. this gives us a bit less than
10139N/A 9 years until it wraps */
10139N/A for (i = 0; i < 48; i += 8)
10139N/A buffer_append_c(buf, (timestamp >> i) & 0xff);
10139N/A
10139N/A buffer_append_c(buf, client->remote_port & 0xff);
10139N/A buffer_append_c(buf, (client->remote_port >> 8) & 0xff);
10139N/A if (IPADDR_IS_V6(&client->ip))
10139N/A buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6));
10139N/A else
10139N/A buffer_append(buf, &client->ip.u.ip4, sizeof(client->ip.u.ip4));
10139N/A base64_encode(buf->data, buf->used, base64_buf);
10139N/A client->session_id = p_strdup(client->pool, str_c(base64_buf));
10139N/A return client->session_id;
10139N/A}
10139N/A
10139N/Astatic struct var_expand_table login_var_expand_empty_tab[] = {
10139N/A { 'u', NULL, "user" },
10139N/A { 'n', NULL, "username" },
10139N/A { 'd', NULL, "domain" },
10139N/A
10139N/A { 's', NULL, "service" },
10139N/A { 'h', NULL, "home" },
10139N/A { 'l', NULL, "lip" },
10139N/A { 'r', NULL, "rip" },
10139N/A { 'p', NULL, "pid" },
10139N/A { 'm', NULL, "mech" },
10139N/A { 'a', NULL, "lport" },
10139N/A { 'b', NULL, "rport" },
10139N/A { 'c', NULL, "secured" },
10139N/A { 'k', NULL, "ssl_security" },
10139N/A { 'e', NULL, "mail_pid" },
10139N/A { '\0', NULL, "session" },
10139N/A { '\0', NULL, "real_lip" },
10139N/A { '\0', NULL, "real_rip" },
10139N/A { '\0', NULL, "real_lport" },
10139N/A { '\0', NULL, "real_rport" },
10139N/A { '\0', NULL, "orig_user" },
10139N/A { '\0', NULL, "orig_username" },
10139N/A { '\0', NULL, "orig_domain" },
10139N/A { '\0', NULL, "auth_user" },
10139N/A { '\0', NULL, "auth_username" },
10139N/A { '\0', NULL, "auth_domain" },
10139N/A { '\0', NULL, "listener" },
10139N/A { '\0', NULL, "local_name" },
10139N/A { '\0', NULL, NULL }
10139N/A};
10139N/A
10139N/Astatic void
10139N/Aget_var_expand_users(struct var_expand_table *tab, const char *user)
10139N/A{
10139N/A unsigned int i;
10139N/A
10139N/A tab[0].value = user;
10139N/A tab[1].value = t_strcut(user, '@');
10139N/A tab[2].value = i_strchr_to_next(user, '@');
10139N/A
10139N/A for (i = 0; i < 3; i++)
10139N/A tab[i].value = str_sanitize(tab[i].value, 80);
10139N/A}
10139N/A
10139N/Astatic const struct var_expand_table *
10139N/Aget_var_expand_table(struct client *client)
10139N/A{
10139N/A struct var_expand_table *tab;
10139N/A
10139N/A tab = t_malloc_no0(sizeof(login_var_expand_empty_tab));
10139N/A memcpy(tab, login_var_expand_empty_tab,
10139N/A sizeof(login_var_expand_empty_tab));
10139N/A
10139N/A if (client->virtual_user != NULL)
10139N/A get_var_expand_users(tab, client->virtual_user);
10139N/A tab[3].value = login_binary->protocol;
10139N/A tab[4].value = getenv("HOME");
10139N/A tab[5].value = net_ip2addr(&client->local_ip);
10139N/A tab[6].value = net_ip2addr(&client->ip);
10139N/A tab[7].value = my_pid;
10139N/A tab[8].value = client->auth_mech_name == NULL ? NULL :
10139N/A str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
10139N/A tab[9].value = dec2str(client->local_port);
10139N/A tab[10].value = dec2str(client->remote_port);
10139N/A if (!client->tls) {
10139N/A tab[11].value = client->secured ? "secured" : NULL;
10139N/A tab[12].value = "";
10139N/A } else {
10139N/A const char *ssl_state =
10139N/A ssl_proxy_is_handshaked(client->ssl_proxy) ?
10139N/A "TLS" : "TLS handshaking";
10139N/A const char *ssl_error =
10139N/A ssl_proxy_get_last_error(client->ssl_proxy);
10139N/A
10139N/A tab[11].value = ssl_error == NULL ? ssl_state :
10139N/A t_strdup_printf("%s: %s", ssl_state, ssl_error);
10139N/A tab[12].value =
10139N/A ssl_proxy_get_security_string(client->ssl_proxy);
10139N/A }
10139N/A tab[13].value = client->mail_pid == 0 ? "" :
10139N/A dec2str(client->mail_pid);
10139N/A tab[14].value = client_get_session_id(client);
10139N/A tab[15].value = net_ip2addr(&client->real_local_ip);
10139N/A tab[16].value = net_ip2addr(&client->real_remote_ip);
10139N/A tab[17].value = dec2str(client->real_local_port);
10139N/A tab[18].value = dec2str(client->real_remote_port);
10139N/A if (client->virtual_user_orig != NULL)
10139N/A get_var_expand_users(tab+19, client->virtual_user_orig);
10139N/A else {
10139N/A tab[19].value = tab[0].value;
10139N/A tab[20].value = tab[1].value;
10139N/A tab[21].value = tab[2].value;
10139N/A }
10139N/A if (client->virtual_auth_user != NULL)
10139N/A get_var_expand_users(tab+22, client->virtual_auth_user);
10139N/A else {
10139N/A tab[22].value = tab[19].value;
10139N/A tab[23].value = tab[20].value;
10139N/A tab[24].value = tab[21].value;
10139N/A }
10139N/A tab[25].value = client->listener_name;
10139N/A tab[26].value = str_sanitize(client->local_name, 256);
10139N/A return tab;
10139N/A}
10139N/A
10139N/Astatic bool have_username_key(const char *str)
10139N/A{
10139N/A char key;
10139N/A
10139N/A for (; *str != '\0'; str++) {
10139N/A if (str[0] == '%' && str[1] != '\0') {
10139N/A str++;
10139N/A 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) {
if (!client->banner_sent) {
/* disconnected by a plugin */
return "";
}
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_common_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_data(struct client *client, const void *data, size_t size)
{
/* FIXME: NULL check is only for backwards compatibility - remove */
if (client->v.send_raw_data != NULL)
client->v.send_raw_data(client, data, size);
else
client_common_send_raw_data(client, data, size);
}
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);
}
void client_common_init(void)
{
i_array_init(&module_hooks, 32);
}
void client_common_deinit(void)
{
array_free(&module_hooks);
}