client-common.c revision 9666d130b63653a5a6d5d2f38ca2df72a5f3f7a7
/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
#include "login-common.h"
#include "array.h"
#include "hostpid.h"
#include "llist.h"
#include "istream.h"
#include "ostream.h"
#include "iostream-ssl.h"
#include "iostream-proxy.h"
#include "iostream-rawlog.h"
#include "process-title.h"
#include "hook-build.h"
#include "buffer.h"
#include "str.h"
#include "strescape.h"
#include "base64.h"
#include "str-sanitize.h"
#include "safe-memset.h"
#include "var-expand.h"
#include "master-interface.h"
#include "master-service.h"
#include "master-service-ssl-settings.h"
#include "master-auth.h"
#include "auth-client.h"
#include "dsasl-client.h"
#include "login-proxy.h"
#include "client-common.h"
static unsigned int clients_count = 0;
static unsigned int client_fd_proxies_count = 0;
struct login_client_module_hooks {
const struct login_client_hooks *hooks;
};
const struct login_client_hooks *hooks)
{
struct login_client_module_hooks *hook;
}
{
const struct login_client_module_hooks *module_hook;
break;
}
}
}
{
const struct login_client_module_hooks *module_hook;
struct hook_build_context *ctx;
} T_END;
}
}
{
const char *user_reason, *destroy_reason;
unsigned int secs;
if (client->master_tag != 0) {
user_reason = "Timeout while finishing login.";
"Timeout while finishing login (waited %u secs)", secs);
"Disconnected for inactivity during authentication.";
"Disconnected: Inactivity during authentication";
user_reason = "Timeout while finishing login.";
"proxy: Logging in to %s:%u timed out "
"(state=%s, duration=%us)",
} else {
user_reason = "Disconnected for inactivity.";
destroy_reason = "Disconnected: Inactivity";
}
}
{
if (login_rawlog_dir != NULL) {
}
}
{
const char *const *net;
unsigned int bits;
return FALSE;
i_error("login_trusted_networks: "
"Invalid network '%s'", *net);
break;
}
return TRUE;
}
return FALSE;
}
struct client *
const struct master_service_connection *conn,
const struct login_settings *set,
const struct master_service_ssl_settings *ssl_set)
{
} else {
}
return client;
}
{
if (last_client == NULL)
else
}
{
if (client->disconnected)
return;
if (!client->login_success &&
const char *extra_reason =
if (extra_reason[0] != '\0')
}
if (!client->login_success) {
} else {
/* Login was successful. We may now be proxying the connection,
so don't disconnect the client until client_unref(). */
}
}
}
{
return;
if (last_client == client)
if (client->master_tag != 0) {
} else {
}
}
/* as soon as this connection is done with proxying
(or whatever), the process will die. there's no need for
authentication anymore, so close the connection.
do this only with initial service_count=1, in case there
are other clients with pending authentications */
}
}
{
}
{
}
{
return TRUE;
if (!client->create_finished) {
return FALSE;
}
if (client->fd_proxying) {
}
i_assert(clients_count > 0);
return FALSE;
}
{
}
void client_destroy_oldest(void)
{
if (last_client == NULL) {
/* we have no clients */
return;
}
/* destroy the last client that hasn't successfully authenticated yet.
this is usually the last client, but don't kill it if it's just
waiting for master to finish its job. */
if (client->master_tag == 0)
break;
}
"Connection queue full");
}
void clients_destroy_all_reason(const char *reason)
{
}
}
void clients_destroy_all(void)
{
clients_destroy_all_reason("Disconnected: Shutting down");
}
void *context)
{
struct ssl_iostream_context *ssl_ctx;
struct ssl_iostream_settings ssl_set;
void **other_sets;
const char *error;
return 0;
"Failed to initialize SSL server context: %s", error);
return -1;
}
return 0;
}
{
struct ssl_iostream_context *ssl_ctx;
struct ssl_iostream_settings ssl_set;
const char *error;
/* If the client cert is invalid, we'll reply NO to the login
command. */
"Failed to initialize SSL server context: %s", error));
return -1;
}
"Failed to initialize SSL connection: %s", error));
return -1;
}
if (!client_does_custom_io(client)) {
}
}
return 0;
}
{
if (client_init_ssl(client) < 0) {
"TLS initialization failed.");
"Disconnected: TLS initialization failed.");
return;
}
}
{
int ret;
return 1;
}
if (ret > 0) {
}
return 1;
}
{
return;
}
if (!client_is_tls_enabled(client)) {
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 */
/* uncork the old fd */
/* the buffer has to be flushed */
} else {
}
}
static void
{
/* Destroy the proxy now. The other side of the proxy is still
unfinished and we don't want to get back here and unreference
the client twice. */
}
{
int fds[2];
/* Plaintext connection - We can send the fd directly to
the post-login process without any proxying. */
*close_fd_r = FALSE;
return 0;
}
/* We'll have to start proxying from now on until either side
disconnects. Create a socketpair where login process is proxying on
one side and the other side is sent to the post-login process. */
return -1;
}
client);
*close_fd_r = TRUE;
return 0;
}
unsigned int clients_get_count(void)
{
return clients_count;
}
unsigned int clients_get_fd_proxies_count(void)
{
return client_fd_proxies_count;
}
struct client *clients_get_first_fd_proxy(void)
{
return client_fd_proxies;
}
const char *value)
{
else
/* prefixing is done by auth process */
}
{
unsigned int i;
return client->session_id;
i_fatal("gettimeofday(): %m");
/* 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)
else
return client->session_id;
}
static struct var_expand_table login_var_expand_empty_tab[] = {
};
static void
{
unsigned int i;
for (i = 0; i < 3; i++)
}
static const struct var_expand_table *
{
struct var_expand_table *tab;
sizeof(login_var_expand_empty_tab));
} else {
const char *ssl_state =
"TLS" : "TLS handshaking";
const char *ssl_error =
}
else {
}
else {
}
return tab;
}
static bool have_username_key(const char *str)
{
char key;
str++;
return TRUE;
}
}
return FALSE;
}
static int
const char **value_r,
const char **error_r ATTR_UNUSED)
{
const char *field_name = data;
unsigned int i;
return 1;
field_name_len) == 0 &&
return 1;
}
}
return 1;
}
static const char *
{
static const struct var_expand_func_table func_table[] = {
{ "passdb", client_var_expand_func_passdb },
};
static bool expand_error_logged = FALSE;
const struct var_expand_table *var_expand_table;
char *const *e;
const char *error;
unsigned int pos;
i_error("Failed to expand log_format_elements=%s: %s",
*e, error);
}
if (have_username_key(*e)) {
/* username is added even if it's empty */
} else {
str_truncate(str2, 0);
&error) <= 0) {
/* we just logged this error above. no need
to do it again. */
}
/* empty %variables, don't add */
continue;
}
}
}
};
str_truncate(str, 0);
i_error("Failed to expand login_log_format=%s: %s",
}
}
{
T_BEGIN {
} T_END;
}
{
T_BEGIN {
} T_END;
}
{
T_BEGIN {
} T_END;
}
{
}
{
return "(client sent an invalid cert)";
return "(client didn't send a cert)";
}
if (!client->notified_auth_ready)
return t_strdup_printf(
"(disconnected before auth was ready, waited %u secs)",
if (client->auth_attempts == 0) {
if (!client->banner_sent) {
/* disconnected by a plugin */
return "";
}
return t_strdup_printf("(no auth attempts in %u secs)",
}
return "(cert required, client didn't start TLS)";
return t_strdup_printf("(client didn't finish SASL auth, "
"waited %u secs)", auth_secs);
}
return t_strdup_printf("(disconnected while authenticating, "
"waited %u secs)", auth_secs);
}
return t_strdup_printf("(disconnected while finishing login, "
"waited %u secs)", auth_secs);
}
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)",
}
switch (client->last_auth_fail) {
return t_strdup_printf(
"(authorization failed, %u attempts in %u secs)",
return "(auth service reported temporary failure)";
return "(user disabled)";
return "(password expired)";
return "(sent invalid base64 in response)";
return "(login disabled)";
return "(tried to use unsupported auth mechanism)";
return "(tried to use disallowed plaintext auth)";
default:
break;
}
return t_strdup_printf("(auth failed, %u attempts in %u secs)",
}
const char *text)
{
if (!client->notified_disconnect) {
}
}
{
if (!client->notified_auth_ready) {
}
}
{
}
{
/* 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.. */
}
}
{
}
{
}
{
case -2:
/* buffer full */
"Input buffer full, aborting");
return FALSE;
case -1:
/* disconnected */
return FALSE;
case 0:
/* nothing new read */
return TRUE;
default:
/* something was read */
return TRUE;
}
}
{
}
void client_common_init(void)
{
}
void client_destroy_fd_proxies(void)
{
while (client_fd_proxies != NULL) {
}
i_assert(client_fd_proxies_count == 0);
}
void client_common_deinit(void)
{
}