summaryrefslogtreecommitdiffstats
path: root/src/submission-login/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/submission-login/client.c')
-rw-r--r--src/submission-login/client.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/submission-login/client.c b/src/submission-login/client.c
new file mode 100644
index 0000000..9922c9d
--- /dev/null
+++ b/src/submission-login/client.c
@@ -0,0 +1,329 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "login-common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "randgen.h"
+#include "hostpid.h"
+#include "safe-memset.h"
+#include "str.h"
+#include "strescape.h"
+#include "master-service.h"
+#include "master-service-ssl-settings.h"
+#include "client.h"
+#include "client-authenticate.h"
+#include "auth-client.h"
+#include "submission-proxy.h"
+#include "submission-login-settings.h"
+
+/* Disconnect client when it sends too many bad commands */
+#define CLIENT_MAX_BAD_COMMANDS 10
+
+static const struct smtp_server_callbacks smtp_callbacks;
+
+static struct smtp_server *smtp_server = NULL;
+
+static void
+client_parse_backend_capabilities(struct submission_client *subm_client )
+{
+ const struct submission_login_settings *set = subm_client->set;
+ const char *const *str;
+
+ if (set->submission_backend_capabilities == NULL) {
+ subm_client->backend_capabilities = SMTP_CAPABILITY_8BITMIME;
+ return;
+ }
+
+ subm_client->backend_capabilities = SMTP_CAPABILITY_NONE;
+ str = t_strsplit_spaces(set->submission_backend_capabilities, " ,");
+ for (; *str != NULL; str++) {
+ enum smtp_capability cap = smtp_capability_find_by_name(*str);
+
+ if (cap == SMTP_CAPABILITY_NONE) {
+ i_warning("Unknown SMTP capability in submission_backend_capabilities: "
+ "%s", *str);
+ continue;
+ }
+
+ subm_client->backend_capabilities |= cap;
+ }
+
+ /* Make sure CHUNKING support is always enabled when BINARYMIME is
+ enabled by explicit configuration. */
+ if (HAS_ALL_BITS(subm_client->backend_capabilities,
+ SMTP_CAPABILITY_BINARYMIME)) {
+ subm_client->backend_capabilities |= SMTP_CAPABILITY_CHUNKING;
+ }
+}
+
+static int submission_login_start_tls(void *conn_ctx,
+ struct istream **input, struct ostream **output)
+{
+ struct submission_client *subm_client = conn_ctx;
+ struct client *client = &subm_client->common;
+
+ client->starttls = TRUE;
+ if (client_init_ssl(client) < 0) {
+ client_notify_disconnect(client,
+ CLIENT_DISCONNECT_INTERNAL_ERROR,
+ "TLS initialization failed.");
+ client_destroy(client,
+ "Disconnected: TLS initialization failed.");
+ return -1;
+ }
+ login_refresh_proctitle();
+
+ *input = client->input;
+ *output = client->output;
+ return 0;
+}
+
+static struct client *submission_client_alloc(pool_t pool)
+{
+ struct submission_client *subm_client;
+
+ subm_client = p_new(pool, struct submission_client, 1);
+ return &subm_client->common;
+}
+
+static void submission_client_create(struct client *client,
+ void **other_sets)
+{
+ static const char *const xclient_extensions[] =
+ { "FORWARD", NULL };
+ struct submission_client *subm_client =
+ container_of(client, struct submission_client, common);
+ struct smtp_server_settings smtp_set;
+
+ subm_client->set = other_sets[0];
+ client_parse_backend_capabilities(subm_client);
+
+ i_zero(&smtp_set);
+ smtp_set.capabilities = SMTP_CAPABILITY_SIZE |
+ SMTP_CAPABILITY_ENHANCEDSTATUSCODES | SMTP_CAPABILITY_AUTH |
+ SMTP_CAPABILITY_XCLIENT;
+ if (client_is_tls_enabled(client))
+ smtp_set.capabilities |= SMTP_CAPABILITY_STARTTLS;
+ smtp_set.hostname = subm_client->set->hostname;
+ smtp_set.login_greeting = client->set->login_greeting;
+ smtp_set.tls_required = !client->secured &&
+ (strcmp(client->ssl_set->ssl, "required") == 0);
+ smtp_set.xclient_extensions = xclient_extensions;
+ smtp_set.command_limits.max_parameters_size = LOGIN_MAX_INBUF_SIZE;
+ smtp_set.command_limits.max_auth_size = LOGIN_MAX_AUTH_BUF_SIZE;
+ smtp_set.debug = client->set->auth_debug;
+
+ subm_client->conn = smtp_server_connection_create_from_streams(
+ smtp_server, client->input, client->output,
+ &client->real_remote_ip, client->real_remote_port,
+ &smtp_set, &smtp_callbacks, subm_client);
+}
+
+static void submission_client_destroy(struct client *client)
+{
+ struct submission_client *subm_client =
+ container_of(client, struct submission_client, common);
+
+ if (subm_client->conn != NULL)
+ smtp_server_connection_close(&subm_client->conn, NULL);
+ i_free_and_null(subm_client->proxy_xclient);
+}
+
+static void submission_client_notify_auth_ready(struct client *client)
+{
+ struct submission_client *subm_client =
+ container_of(client, struct submission_client, common);
+
+ client->banner_sent = TRUE;
+ smtp_server_connection_start(subm_client->conn);
+}
+
+static void
+submission_client_notify_disconnect(struct client *_client,
+ enum client_disconnect_reason reason,
+ const char *text)
+{
+ struct submission_client *client =
+ container_of(_client, struct submission_client, common);
+ struct smtp_server_connection *conn;
+
+ conn = client->conn;
+ client->conn = NULL;
+ if (conn != NULL) {
+ switch (reason) {
+ case CLIENT_DISCONNECT_TIMEOUT:
+ smtp_server_connection_terminate(&conn, "4.4.2", text);
+ break;
+ case CLIENT_DISCONNECT_SYSTEM_SHUTDOWN:
+ smtp_server_connection_terminate(&conn, "4.3.2", text);
+ break;
+ case CLIENT_DISCONNECT_INTERNAL_ERROR:
+ default:
+ smtp_server_connection_terminate(&conn, "4.0.0", text);
+ break;
+ }
+ }
+}
+
+static void
+client_connection_cmd_xclient(void *context,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_proxy_data *data)
+{
+ unsigned int i;
+
+ struct submission_client *client = context;
+
+ if (data->source_ip.family != 0)
+ client->common.ip = data->source_ip;
+ if (data->source_port != 0)
+ client->common.remote_port = data->source_port;
+ if (data->ttl_plus_1 > 0)
+ client->common.proxy_ttl = data->ttl_plus_1 - 1;
+ if (data->session != NULL) {
+ client->common.session_id =
+ p_strdup(client->common.pool, data->session);
+ }
+
+ for (i = 0; i < data->extra_fields_count; i++) {
+ const char *name = data->extra_fields[i].name;
+ const char *value = data->extra_fields[i].value;
+
+ if (strcasecmp(name, "FORWARD") == 0) {
+ size_t value_len = strlen(value);
+ if (client->common.forward_fields != NULL) {
+ str_truncate(client->common.forward_fields, 0);
+ } else {
+ client->common.forward_fields = str_new(
+ client->common.preproxy_pool,
+ MAX_BASE64_DECODED_SIZE(value_len));
+ if (base64_decode(value, value_len, NULL,
+ client->common.forward_fields) < 0) {
+ smtp_server_reply(cmd, 501, "5.5.4",
+ "Invalid FORWARD parameter");
+ }
+ }
+ }
+ }
+}
+
+static void client_connection_disconnect(void *context, const char *reason)
+{
+ struct submission_client *client = context;
+
+ client->pending_auth = NULL;
+ client_disconnect(&client->common, reason,
+ !client->common.login_success);
+}
+
+static void client_connection_free(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_set.reason_code_module = "submission";
+ /* Pre-auth state is always logged either as GREETING or READY.
+ It's not very useful. */
+ smtp_server_set.no_state_in_reason = TRUE;
+ smtp_server = smtp_server_init(&smtp_server_set);
+ smtp_server_command_override(smtp_server, "MAIL", cmd_mail,
+ SMTP_SERVER_CMD_FLAG_PREAUTH);
+}
+
+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_free = client_connection_free,
+
+ .conn_is_trusted = client_connection_is_trusted
+};
+
+static struct client_vfuncs submission_client_vfuncs = {
+ .alloc = submission_client_alloc,
+ .create = submission_client_create,
+ .destroy = submission_client_destroy,
+ .notify_auth_ready = submission_client_notify_auth_ready,
+ .notify_disconnect = submission_client_notify_disconnect,
+ .auth_send_challenge = submission_client_auth_send_challenge,
+ .auth_result = submission_client_auth_result,
+ .proxy_reset = submission_proxy_reset,
+ .proxy_parse_line = submission_proxy_parse_line,
+ .proxy_failed = submission_proxy_failed,
+ .proxy_get_state = submission_proxy_get_state,
+};
+
+static struct login_binary submission_login_binary = {
+ .protocol = "submission",
+ .process_name = "submission-login",
+ .default_port = 587,
+
+ .event_category = {
+ .name = "submission",
+ },
+
+ .client_vfuncs = &submission_client_vfuncs,
+ .preinit = submission_login_preinit,
+ .init = submission_login_init,
+ .deinit = submission_login_deinit,
+
+ .sasl_support_final_reply = FALSE,
+ .anonymous_login_acceptable = FALSE,
+};
+
+int main(int argc, char *argv[])
+{
+ return login_binary_run(&submission_login_binary, argc, argv);
+}