client.c revision 0afd9a9acab584e770ffcd6a0e1e02e2d18d360a
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "login-common.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "base64.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "buffer.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "ioloop.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "istream.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "ostream.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "randgen.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "hostpid.h"
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody#include "safe-memset.h"
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody#include "str.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "strescape.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "master-service.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "master-service-ssl-settings.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "client.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "client-authenticate.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "auth-client.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "submission-proxy.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "submission-login-settings.h"
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen/* Disconnect client when it sends too many bad commands */
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#define CLIENT_MAX_BAD_COMMANDS 10
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic const struct smtp_server_callbacks smtp_callbacks;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic struct smtp_server *smtp_server = NULL;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void submission_login_start_tls(void *conn_ctx,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct istream **input, struct ostream **output)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *subm_client = conn_ctx;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct client *client = &subm_client->common;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client->starttls = TRUE;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (client_init_ssl(client) < 0) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client_notify_disconnect(client,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen CLIENT_DISCONNECT_INTERNAL_ERROR,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen "TLS initialization failed.");
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client_destroy(client,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen "Disconnected: TLS initialization failed.");
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen login_refresh_proctitle();
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen *input = client->input;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen *output = client->output;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic struct client *submission_client_alloc(pool_t pool)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *subm_client;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen subm_client = p_new(pool, struct submission_client, 1);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return &subm_client->common;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void submission_client_create(struct client *client,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen void **other_sets)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen static const char *const xclient_extensions[] =
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen { "SESSION", "FORWARD", NULL };
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *subm_client =
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen (struct submission_client *)client;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct smtp_server_settings smtp_set;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen subm_client->set = other_sets[0];
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen i_zero(&smtp_set);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.capabilities = SMTP_CAPABILITY_SIZE |
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen SMTP_CAPABILITY_ENHANCEDSTATUSCODES | SMTP_CAPABILITY_AUTH;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (client_is_tls_enabled(client))
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.capabilities |= SMTP_CAPABILITY_STARTTLS;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.hostname = subm_client->set->hostname;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.login_greeting = client->set->login_greeting;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.tls_required = (strcmp(client->ssl_set->ssl, "required") == 0);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.xclient_extensions = xclient_extensions;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_set.debug = client->set->auth_debug;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen subm_client->conn = smtp_server_connection_create_from_streams(
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server, client->input, client->output,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen &client->real_remote_ip, client->real_remote_port,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen &smtp_set, &smtp_callbacks, subm_client);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void submission_client_destroy(struct client *client)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *subm_client =
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen (struct submission_client *)client;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (subm_client->conn != NULL)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server_connection_close(&subm_client->conn, NULL);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen i_free_and_null(subm_client->proxy_xclient);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void submission_client_notify_auth_ready(struct client *client)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *subm_client =
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen (struct submission_client *)client;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server_connection_start(subm_client->conn, FALSE);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainensubmission_client_notify_disconnect(struct client *_client,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen enum client_disconnect_reason reason,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen const char *text)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *client = (struct submission_client *)_client;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct smtp_server_connection *conn;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen conn = client->conn;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client->conn = NULL;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (conn != NULL) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen switch (reason) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case CLIENT_DISCONNECT_TIMEOUT:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server_connection_terminate(&conn, "4.4.2", text);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case CLIENT_DISCONNECT_SYSTEM_SHUTDOWN:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server_connection_terminate(&conn, "4.3.2", text);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case CLIENT_DISCONNECT_INTERNAL_ERROR:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen default:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server_connection_terminate(&conn, "4.0.0", text);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenclient_connection_cmd_xclient(void *context,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct smtp_server_cmd_ctx *cmd,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct smtp_proxy_data *data)
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody{
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody unsigned int i;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *client = context;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client->common.ip = data->source_ip;
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody client->common.remote_port = data->source_port;
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client->common.proxy_ttl = data->ttl_plus_1;
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody for (i = 0; i < data->extra_fields_count; i++) {
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody const char *name = data->extra_fields[i].name;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen const char *value = data->extra_fields[i].value;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (strcasecmp(name, "FORWARD") == 0) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen size_t value_len = strlen(value);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (client->common.forward_fields != NULL) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen str_truncate(client->common.forward_fields, 0);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen } else {
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody client->common.forward_fields = str_new(
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody client->common.preproxy_pool,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen MAX_BASE64_DECODED_SIZE(value_len));
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (base64_decode(value, value_len, NULL,
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody client->common.forward_fields) < 0) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen smtp_server_reply(cmd, 501, "5.5.4",
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen "Invalid FORWARD parameter");
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody } else if (strcasecmp(name, "SESSION") == 0) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (client->common.session_id != NULL)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen continue;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen client->common.session_id =
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody p_strdup(client->common.pool, value);
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody }
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody }
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void client_connection_disconnect(void *context, const char *reason)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct submission_client *client = context;
client_disconnect(&client->common, reason);
}
static void client_connection_destroy(void *context)
{
struct submission_client *client = context;
if (client->conn == NULL)
return;
client->conn = NULL;
client_destroy(&client->common, NULL);
}
static bool client_connection_is_trusted(void *context)
{
struct submission_client *client = context;
return client->common.trusted;
}
static void submission_login_die(void)
{
/* do nothing. submission connections typically die pretty quick anyway.
*/
}
static void submission_login_preinit(void)
{
login_set_roots = submission_login_setting_roots;
}
static void submission_login_init(void)
{
struct smtp_server_settings smtp_server_set;
/* override the default login_die() */
master_service_set_die_callback(master_service, submission_login_die);
/* initialize SMTP server */
i_zero(&smtp_server_set);
smtp_server_set.protocol = SMTP_PROTOCOL_SMTP;
smtp_server_set.max_pipelined_commands = 5;
smtp_server_set.max_bad_commands = CLIENT_MAX_BAD_COMMANDS;
smtp_server = smtp_server_init(&smtp_server_set);
}
static void submission_login_deinit(void)
{
clients_destroy_all();
smtp_server_deinit(&smtp_server);
}
static const struct smtp_server_callbacks smtp_callbacks = {
.conn_cmd_helo = cmd_helo,
.conn_start_tls = submission_login_start_tls,
.conn_cmd_auth = cmd_auth,
.conn_cmd_auth_continue = cmd_auth_continue,
.conn_cmd_xclient = client_connection_cmd_xclient,
.conn_disconnect = client_connection_disconnect,
.conn_destroy = client_connection_destroy,
.conn_is_trusted = client_connection_is_trusted
};
static struct client_vfuncs submission_client_vfuncs = {
submission_client_alloc,
submission_client_create,
submission_client_destroy,
submission_client_notify_auth_ready,
submission_client_notify_disconnect,
NULL,
NULL,
NULL,
NULL,
submission_client_auth_send_challenge,
NULL,
submission_client_auth_result,
submission_proxy_reset,
submission_proxy_parse_line,
submission_proxy_error,
submission_proxy_get_state,
NULL,
NULL,
NULL
};
static const struct login_binary submission_login_binary = {
.protocol = "submission",
.process_name = "submission-login",
.default_port = 587,
.client_vfuncs = &submission_client_vfuncs,
.preinit = submission_login_preinit,
.init = submission_login_init,
.deinit = submission_login_deinit,
.sasl_support_final_reply = FALSE
};
int main(int argc, char *argv[])
{
return login_binary_run(&submission_login_binary, argc, argv);
}