]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lmtp: Support forwarding passdb's forward_* fields via RCPT TO XRCPTFORWARD parameter.
authorStephan Bosch <stephan.bosch@open-xchange.com>
Fri, 1 May 2020 15:26:14 +0000 (17:26 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Wed, 27 May 2020 05:32:15 +0000 (05:32 +0000)
src/lmtp/lmtp-client.c
src/lmtp/lmtp-commands.c
src/lmtp/lmtp-common.h
src/lmtp/lmtp-local.c
src/lmtp/lmtp-proxy.c
src/lmtp/lmtp-recipient.h

index 3b3f0ec6d9ef54e6eb776af0b7c691118ef72f4a..47c0c88a2400c1fbc3c6289096dd5ed5829eed1e 100644 (file)
@@ -147,6 +147,10 @@ static void client_read_settings(struct client *client, bool ssl)
 struct client *client_create(int fd_in, int fd_out,
                             const struct master_service_connection *conn)
 {
+       static const char *rcpt_param_extensions[] = {
+               LMTP_RCPT_FORWARD_PARAMETER, NULL };
+       static const struct smtp_capability_extra cap_rcpt_forward = {
+               .name = LMTP_RCPT_FORWARD_CAPABILITY };
        enum lmtp_client_workarounds workarounds;
        struct smtp_server_settings lmtp_set;
        struct client *client;
@@ -192,6 +196,7 @@ struct client *client_create(int fd_in, int fd_out,
        lmtp_set.hostname = client->unexpanded_lda_set->hostname;
        lmtp_set.login_greeting = client->lmtp_set->login_greeting;
        lmtp_set.max_message_size = (uoff_t)-1;
+       lmtp_set.rcpt_param_extensions = rcpt_param_extensions;
        lmtp_set.rcpt_domain_optional = TRUE;
        lmtp_set.max_client_idle_time_msecs = CLIENT_IDLE_TIMEOUT_MSECS;
        lmtp_set.rawlog_dir = client->lmtp_set->lmtp_rawlog_dir;
@@ -211,6 +216,10 @@ struct client *client_create(int fd_in, int fd_out,
                lmtp_server, fd_in, fd_out,
                &conn->remote_ip, conn->remote_port, conn->ssl,
                &lmtp_set, &lmtp_callbacks, client);
+       if (smtp_server_connection_is_trusted(client->conn)) {
+               smtp_server_connection_add_extra_capability(
+                       client->conn, &cap_rcpt_forward);
+       }
 
        DLLIST_PREPEND(&clients, client);
        clients_count++;
index 3a9df155e91b498ffea00967ed776625614af888..cbafa644cc969f23f8277e360dd5cb4d12d52c30 100644 (file)
@@ -46,6 +46,42 @@ int client_default_cmd_mail(struct client *client,
  * RCPT command
  */
 
+static int
+cmd_rcpt_handle_forward_fields(struct smtp_server_cmd_ctx *cmd,
+                              struct lmtp_recipient *lrcpt)
+{
+       struct smtp_server_recipient *rcpt = lrcpt->rcpt;
+       string_t *xforward;
+       const char *error;
+       int ret;
+
+       ret = smtp_params_rcpt_decode_extra(&rcpt->params,
+                                           LMTP_RCPT_FORWARD_PARAMETER,
+                                           &xforward, FALSE, &error);
+       if (ret < 0) {
+               smtp_server_reply(cmd, 501, "5.5.4",
+                                 "Invalid "LMTP_RCPT_FORWARD_PARAMETER"= "
+                                 "parameter: %s", error);
+               return -1;
+       }
+       if (ret == 0)
+               return 0;
+
+       /* Check the real IP rather than the proxied client IP, since XCLIENT
+          command will update that, thereby making it untrusted. Unlike the
+          XCLIENT command, the RCPT forward parameter needs to be used after
+          the XCLIENT is first issued. */
+       if (!smtp_server_connection_is_trusted(rcpt->conn)) {
+               smtp_server_reply(cmd, 550, "5.7.14",
+                                 "Unacceptable "LMTP_RCPT_FORWARD_PARAMETER"= "
+                                 "parameter: You are not from trusted IP");
+               return -1;
+       }
+
+       lrcpt->forward_fields = p_strdup(rcpt->pool, str_c(xforward));
+       return 0;
+}
+
 int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
             struct smtp_server_recipient *rcpt)
 {
@@ -54,6 +90,9 @@ int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
 
        lrcpt = lmtp_recipient_create(client, rcpt);
 
+       if (cmd_rcpt_handle_forward_fields(cmd, lrcpt) < 0)
+               return -1;
+
        return client->v.cmd_rcpt(client, cmd, lrcpt);
 }
 
index dba2e5642474437a86c72f0a1268d63875218197..8e1729c109bfb367643043d32a647e19c89489a4 100644 (file)
@@ -11,6 +11,9 @@
 #include "lmtp-client.h"
 #include "lmtp-settings.h"
 
+#define LMTP_RCPT_FORWARD_CAPABILITY "XRCPTFORWARD"
+#define LMTP_RCPT_FORWARD_PARAMETER  "XRCPTFORWARD"
+
 typedef void lmtp_client_created_func_t(struct client **client);
 
 extern lmtp_client_created_func_t *hook_client_created;
index 9d6072d24965c8764fd027ad757a773b91630690..3ef3bcaa498581ede1a4567ea4b78d31856d5455 100644 (file)
@@ -317,6 +317,7 @@ int lmtp_local_rcpt(struct client *client, struct smtp_server_cmd_ctx *cmd,
                smtp_server_connection_is_ssl_secured(client->conn);
        input.conn_secured = input.conn_ssl_secured ||
                smtp_server_connection_is_trusted(client->conn);
+       input.forward_fields = lrcpt->forward_fields;
 
        event_add_str(rcpt->event, "session", session_id);
        input.parent_event = rcpt->event;
index 6c90ad1ad0d03fc0451f18c1b5c67f29f8693fe0..d1d241183741393377b9d78357c4a3eef61ad944 100644 (file)
@@ -6,6 +6,7 @@
 #include "ostream.h"
 #include "iostream-ssl.h"
 #include "str.h"
+#include "strescape.h"
 #include "time-util.h"
 #include "smtp-common.h"
 #include "smtp-params.h"
@@ -50,6 +51,9 @@ struct lmtp_proxy_recipient {
 
        struct smtp_address *address;
 
+       const unsigned char *forward_fields;
+       size_t forward_fields_size;
+
        bool rcpt_to_failed:1;
        bool data_reply_received:1;
 };
@@ -99,6 +103,9 @@ static struct lmtp_proxy *
 lmtp_proxy_init(struct client *client,
                struct smtp_server_transaction *trans)
 {
+       const char *extra_capabilities[] = {
+               LMTP_RCPT_FORWARD_CAPABILITY,
+               NULL };
        struct smtp_client_settings lmtp_set;
        struct lmtp_proxy *proxy;
 
@@ -110,6 +117,7 @@ lmtp_proxy_init(struct client *client,
 
        i_zero(&lmtp_set);
        lmtp_set.my_hostname = client->my_domain;
+       lmtp_set.extra_capabilities = extra_capabilities;
        lmtp_set.dns_client_socket_path = dns_client_socket_path;
        lmtp_set.max_reply_size = LMTP_MAX_REPLY_SIZE;
        lmtp_set.rawlog_dir = client->lmtp_set->lmtp_proxy_rawlog_dir;
@@ -200,6 +208,16 @@ lmtp_proxy_connection_init_ssl(struct lmtp_proxy_connection *conn,
                *ssl_mode_r = SMTP_CLIENT_SSL_MODE_STARTTLS;
 }
 
+static bool
+lmtp_proxy_connection_has_rcpt_forward(struct lmtp_proxy_connection *conn)
+{
+       const struct smtp_capability_extra *cap_extra =
+               smtp_client_connection_get_extra_capability(
+                       conn->lmtp_conn, LMTP_RCPT_FORWARD_CAPABILITY);
+
+       return (cap_extra != NULL);
+}
+
 static struct lmtp_proxy_connection *
 lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
                          const struct lmtp_proxy_rcpt_settings *set)
@@ -256,6 +274,8 @@ lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
                        conn->set.host, conn->set.port,
                        ssl_mode, &lmtp_set);
        }
+       smtp_client_connection_accept_extra_capability(
+               conn->lmtp_conn, LMTP_RCPT_FORWARD_CAPABILITY);
        smtp_client_connection_connect(conn->lmtp_conn, NULL, NULL);
 
        conn->lmtp_trans = smtp_client_transaction_create(
@@ -475,7 +495,7 @@ lmtp_proxy_rcpt_login_cb(const struct smtp_reply *proxy_reply, void *context)
        struct smtp_reply reply;
        struct smtp_client_transaction_rcpt *relay_rcpt;
        struct smtp_params_rcpt *rcpt_params = &rcpt->params;
-       bool add_orcpt_param = FALSE;
+       bool add_orcpt_param = FALSE, add_xrcptforward_param = FALSE;
        pool_t param_pool;
 
        if (!lmtp_proxy_handle_reply(lprcpt, proxy_reply, &reply))
@@ -492,9 +512,14 @@ lmtp_proxy_rcpt_login_cb(const struct smtp_reply *proxy_reply, void *context)
            !smtp_address_equals(lprcpt->address, rcpt->path))
                add_orcpt_param = TRUE;
 
+       /* Add forward fields parameter when passdb returned forward_* fields */
+       if (lprcpt->forward_fields != NULL &&
+           lmtp_proxy_connection_has_rcpt_forward(conn))
+               add_xrcptforward_param = TRUE;
+
        /* Copy params when changes are pending */
        param_pool = NULL;
-       if (add_orcpt_param) {
+       if (add_orcpt_param || add_xrcptforward_param) {
                param_pool = pool_datastack_create();
                rcpt_params = p_new(param_pool, struct smtp_params_rcpt, 1);
                smtp_params_rcpt_copy(param_pool, rcpt_params, &rcpt->params);
@@ -505,6 +530,12 @@ lmtp_proxy_rcpt_login_cb(const struct smtp_reply *proxy_reply, void *context)
                smtp_params_rcpt_set_orcpt(rcpt_params, param_pool,
                                           rcpt->path);
        }
+       /* Add forward fields parameter */
+       if (add_xrcptforward_param) {
+               smtp_params_rcpt_encode_extra(
+                       rcpt_params, param_pool, LMTP_RCPT_FORWARD_PARAMETER,
+                       lprcpt->forward_fields, lprcpt->forward_fields_size);
+       }
 
        smtp_server_recipient_add_hook(
                rcpt, SMTP_SERVER_RECIPIENT_HOOK_APPROVED,
@@ -535,6 +566,7 @@ int lmtp_proxy_rcpt(struct client *client,
        const char *const *fields, *errstr, *orig_username = username;
        struct smtp_proxy_data proxy_data;
        struct smtp_address *user;
+       string_t *fwfields;
        pool_t auth_pool;
        int ret;
 
@@ -555,6 +587,7 @@ int lmtp_proxy_rcpt(struct client *client,
        info.real_local_port = client->real_local_port;
        info.remote_port = client->remote_port;
        info.real_remote_port = client->real_remote_port;
+       info.forward_fields = lrcpt->forward_fields;
 
        // FIXME: make this async
        auth_pool = pool_alloconly_create("auth lookup", 1024);
@@ -636,7 +669,6 @@ int lmtp_proxy_rcpt(struct client *client,
                client->proxy = lmtp_proxy_init(client, trans);
 
        conn = lmtp_proxy_get_connection(client->proxy, &set);
-       pool_unref(&auth_pool);
 
        lprcpt = p_new(rcpt->pool, struct lmtp_proxy_recipient, 1);
        lprcpt->rcpt = lrcpt;
@@ -646,6 +678,27 @@ int lmtp_proxy_rcpt(struct client *client,
        lrcpt->type = LMTP_RECIPIENT_TYPE_PROXY;
        lrcpt->backend_context = lprcpt;
 
+       /* Copy forward fields returned from passdb */
+       fwfields = NULL;
+       for (const char *const *ptr = fields; *ptr != NULL; ptr++) {
+               if (strncasecmp(*ptr, "forward_", 8) != 0)
+                       continue;
+
+               if (fwfields == NULL)
+                       fwfields = t_str_new(128);
+               else
+                       str_append_c(fwfields, '\t');
+
+               str_append_tabescaped(fwfields, (*ptr) + 8);
+       }
+       if (fwfields != NULL) {
+               lprcpt->forward_fields = p_memdup(
+                       rcpt->pool, str_data(fwfields), str_len(fwfields));
+               lprcpt->forward_fields_size = str_len(fwfields);
+       }
+
+       pool_unref(&auth_pool);
+
        smtp_client_connection_connect(conn->lmtp_conn,
                                       lmtp_proxy_rcpt_login_cb, lprcpt);
        return 1;
index 3614a8b9377e04ca4696169ef0138b5c9bc19aef..8d11124057b744f3b8118ba47b4de0c07e28374b 100644 (file)
@@ -20,6 +20,8 @@ struct lmtp_recipient {
        enum lmtp_recipient_type type;
        void *backend_context;
 
+       const char *forward_fields;
+
        /* Module-specific contexts. */
        ARRAY(union lmtp_recipient_module_context *) module_contexts;
 };