From: Stephan Bosch Date: Fri, 1 May 2020 15:26:14 +0000 (+0200) Subject: lmtp: Support forwarding passdb's forward_* fields via RCPT TO XRCPTFORWARD parameter. X-Git-Tag: 2.3.13~541 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0c10ac753410c4fe24c7e6fbcaf6d9f28556a523;p=thirdparty%2Fdovecot%2Fcore.git lmtp: Support forwarding passdb's forward_* fields via RCPT TO XRCPTFORWARD parameter. --- diff --git a/src/lmtp/lmtp-client.c b/src/lmtp/lmtp-client.c index 3b3f0ec6d9..47c0c88a24 100644 --- a/src/lmtp/lmtp-client.c +++ b/src/lmtp/lmtp-client.c @@ -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++; diff --git a/src/lmtp/lmtp-commands.c b/src/lmtp/lmtp-commands.c index 3a9df155e9..cbafa644cc 100644 --- a/src/lmtp/lmtp-commands.c +++ b/src/lmtp/lmtp-commands.c @@ -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); } diff --git a/src/lmtp/lmtp-common.h b/src/lmtp/lmtp-common.h index dba2e56424..8e1729c109 100644 --- a/src/lmtp/lmtp-common.h +++ b/src/lmtp/lmtp-common.h @@ -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; diff --git a/src/lmtp/lmtp-local.c b/src/lmtp/lmtp-local.c index 9d6072d249..3ef3bcaa49 100644 --- a/src/lmtp/lmtp-local.c +++ b/src/lmtp/lmtp-local.c @@ -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; diff --git a/src/lmtp/lmtp-proxy.c b/src/lmtp/lmtp-proxy.c index 6c90ad1ad0..d1d2411837 100644 --- a/src/lmtp/lmtp-proxy.c +++ b/src/lmtp/lmtp-proxy.c @@ -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; diff --git a/src/lmtp/lmtp-recipient.h b/src/lmtp/lmtp-recipient.h index 3614a8b937..8d11124057 100644 --- a/src/lmtp/lmtp-recipient.h +++ b/src/lmtp/lmtp-recipient.h @@ -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; };