diff options
Diffstat (limited to '')
-rw-r--r-- | src/lmtp/lmtp-local.c | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/src/lmtp/lmtp-local.c b/src/lmtp/lmtp-local.c new file mode 100644 index 0000000..c20b619 --- /dev/null +++ b/src/lmtp/lmtp-local.c @@ -0,0 +1,766 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "lmtp-common.h" +#include "str.h" +#include "istream.h" +#include "strescape.h" +#include "time-util.h" +#include "hostpid.h" +#include "var-expand.h" +#include "restrict-access.h" +#include "anvil-client.h" +#include "settings-parser.h" +#include "mail-storage.h" +#include "mail-storage-service.h" +#include "mail-namespace.h" +#include "mail-deliver.h" +#include "mail-autoexpunge.h" +#include "index/raw/raw-storage.h" +#include "smtp-common.h" +#include "smtp-params.h" +#include "smtp-address.h" +#include "smtp-submit-settings.h" +#include "lda-settings.h" +#include "lmtp-settings.h" +#include "lmtp-recipient.h" +#include "lmtp-local.h" + +struct lmtp_local_recipient { + struct lmtp_recipient *rcpt; + + char *detail; + + struct mail_storage_service_user *service_user; + struct anvil_query *anvil_query; + + struct lmtp_local_recipient *duplicate; + + bool anvil_connect_sent:1; +}; + +struct lmtp_local { + struct client *client; + + ARRAY(struct lmtp_local_recipient *) rcpt_to; + + struct mail *raw_mail, *first_saved_mail; + struct mail_user *rcpt_user; +}; + +/* + * LMTP local + */ + +static struct lmtp_local * +lmtp_local_init(struct client *client) +{ + struct lmtp_local *local; + + local = i_new(struct lmtp_local, 1); + local->client = client; + i_array_init(&local->rcpt_to, 8); + + return local; +} + +void lmtp_local_deinit(struct lmtp_local **_local) +{ + struct lmtp_local *local = *_local; + + *_local = NULL; + + if (array_is_created(&local->rcpt_to)) + array_free(&local->rcpt_to); + + if (local->raw_mail != NULL) { + struct mailbox_transaction_context *raw_trans = + local->raw_mail->transaction; + struct mailbox *raw_box = local->raw_mail->box; + + mail_free(&local->raw_mail); + mailbox_transaction_rollback(&raw_trans); + mailbox_free(&raw_box); + } + + i_free(local); +} + +/* + * Recipient + */ + +static void +lmtp_local_rcpt_anvil_disconnect(struct lmtp_local_recipient *llrcpt) +{ + const struct mail_storage_service_input *input; + + if (!llrcpt->anvil_connect_sent) + return; + llrcpt->anvil_connect_sent = FALSE; + + input = mail_storage_service_user_get_input(llrcpt->service_user); + master_service_anvil_send(master_service, t_strconcat( + "DISCONNECT\t", my_pid, "\t", master_service_get_name(master_service), + "/", input->username, "\n", NULL)); +} + +static void +lmtp_local_rcpt_destroy(struct smtp_server_recipient *rcpt ATTR_UNUSED, + struct lmtp_local_recipient *llrcpt) +{ + if (llrcpt->anvil_query != NULL) + anvil_client_query_abort(anvil, &llrcpt->anvil_query); + lmtp_local_rcpt_anvil_disconnect(llrcpt); + mail_storage_service_user_unref(&llrcpt->service_user); +} + +static void +lmtp_local_rcpt_reply_overquota(struct lmtp_local_recipient *llrcpt, + const char *error) +{ + struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt; + struct lda_settings *lda_set = + mail_storage_service_user_get_set(llrcpt->service_user)[2]; + + if (lda_set->quota_full_tempfail) + smtp_server_recipient_reply(rcpt, 452, "4.2.2", "%s", error); + else + smtp_server_recipient_reply(rcpt, 552, "5.2.2", "%s", error); +} + +static void ATTR_FORMAT(4,5) +lmtp_local_rcpt_fail_all(struct lmtp_local *local, + unsigned int status, const char *enh_code, + const char *fmt, ...) +{ + struct lmtp_local_recipient *const *llrcpts; + const char *msg; + unsigned int count, i; + va_list args; + + va_start(args, fmt); + msg = t_strdup_vprintf(fmt, args); + va_end(args); + + llrcpts = array_get(&local->rcpt_to, &count); + for (i = 0; i < count; i++) { + struct smtp_server_recipient *rcpt = llrcpts[i]->rcpt->rcpt; + + smtp_server_recipient_reply(rcpt, status, enh_code, "%s", msg); + } +} + +/* + * RCPT command + */ + +static int +lmtp_local_rcpt_check_quota(struct lmtp_local_recipient *llrcpt) +{ + struct client *client = llrcpt->rcpt->client; + struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt; + struct smtp_address *address = rcpt->path; + struct mail_user *user; + struct mail_namespace *ns; + struct mailbox *box; + struct mailbox_status status; + enum mail_error mail_error; + const char *error; + int ret; + + if (!client->lmtp_set->lmtp_rcpt_check_quota) + return 0; + + /* mail user will be created second time when mail is saved, + so it's session_id needs to be different, + but second time session_id needs to be the same as rcpt session_id and + mail user session id for the first rcpt should not overlap with session id + of the second recipient, so add custom ":quota" suffix to the session_id without + session_id counter increment, so next time mail user will get + the same session id as rcpt */ + ret = mail_storage_service_next_with_session_suffix(storage_service, + llrcpt->service_user, + "quota", + &user, &error); + + if (ret < 0) { + e_error(rcpt->event, "Failed to initialize user %s: %s", + smtp_address_encode(address), error); + ret = -1; + } else { + /* Set the log prefix for the user. The default log prefix is + automatically restored later when user context gets + deactivated. */ + i_set_failure_prefix("%s", + mail_storage_service_user_get_log_prefix(llrcpt->service_user)); + ns = mail_namespace_find_inbox(user->namespaces); + box = mailbox_alloc(ns->list, "INBOX", 0); + ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status); + if (ret < 0) { + error = mailbox_get_last_error(box, &mail_error); + if (mail_error == MAIL_ERROR_NOQUOTA) { + lmtp_local_rcpt_reply_overquota(llrcpt, error); + } else { + e_error(rcpt->event, + "mailbox_get_status(%s, STATUS_CHECK_OVER_QUOTA) " + "failed: %s", + mailbox_get_vname(box), + mailbox_get_last_internal_error(box, NULL)); + } + ret = -1; + } + mailbox_free(&box); + mail_user_deinit(&user); + mail_storage_service_io_deactivate_user(llrcpt->service_user); + } + + if (ret < 0 && !smtp_server_recipient_is_replied(rcpt)) { + smtp_server_recipient_reply(rcpt, 451, "4.3.0", + "Temporary internal error"); + } + return ret; +} + +static void +lmtp_local_rcpt_approved(struct smtp_server_recipient *rcpt, + struct lmtp_local_recipient *llrcpt) +{ + struct client *client = llrcpt->rcpt->client; + struct lmtp_recipient *drcpt; + + /* resolve duplicate recipient */ + drcpt = lmtp_recipient_find_duplicate(llrcpt->rcpt, rcpt->trans); + if (drcpt != NULL) { + i_assert(drcpt->type == LMTP_RECIPIENT_TYPE_LOCAL); + llrcpt->duplicate = drcpt->backend_context; + i_assert(llrcpt->duplicate->duplicate == NULL); + } + + /* add to local recipients */ + array_push_back(&client->local->rcpt_to, &llrcpt); +} + +static bool +lmtp_local_rcpt_anvil_finish(struct lmtp_local_recipient *llrcpt) +{ + struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt; + struct smtp_server_cmd_ctx *cmd = rcpt->cmd; + + if (lmtp_local_rcpt_check_quota(llrcpt) < 0) + return FALSE; + + smtp_server_cmd_rcpt_reply_success(cmd); + return TRUE; +} + +static void +lmtp_local_rcpt_anvil_cb(const char *reply, void *context) +{ + struct lmtp_local_recipient *llrcpt = + (struct lmtp_local_recipient *)context; + struct client *client = llrcpt->rcpt->client; + struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt; + const struct mail_storage_service_input *input; + unsigned int parallel_count = 0; + + llrcpt->anvil_query = NULL; + if (reply == NULL) { + /* lookup failed */ + } else if (str_to_uint(reply, ¶llel_count) < 0) { + e_error(rcpt->event, "Invalid reply from anvil: %s", reply); + } + + if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) { + smtp_server_recipient_reply( + rcpt, 451, "4.3.0", + "Too many concurrent deliveries for user"); + } else if (lmtp_local_rcpt_anvil_finish(llrcpt)) { + llrcpt->anvil_connect_sent = TRUE; + input = mail_storage_service_user_get_input(llrcpt->service_user); + master_service_anvil_send(master_service, t_strconcat( + "CONNECT\t", my_pid, "\t", master_service_get_name(master_service), + "/", input->username, "\n", NULL)); + } +} + +int lmtp_local_rcpt(struct client *client, + struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, + struct lmtp_recipient *lrcpt, const char *username, + const char *detail) +{ + struct smtp_server_recipient *rcpt = lrcpt->rcpt; + struct lmtp_local_recipient *llrcpt; + struct mail_storage_service_input input; + struct mail_storage_service_user *service_user; + const char *error = NULL; + int ret = 0; + + i_zero(&input); + input.module = input.service = "lmtp"; + input.username = username; + input.local_ip = client->local_ip; + input.remote_ip = client->remote_ip; + input.local_port = client->local_port; + input.remote_port = client->remote_port; + input.session_id = lrcpt->session_id; + input.conn_ssl_secured = + 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; + input.event_parent = rcpt->event; + + ret = mail_storage_service_lookup(storage_service, &input, + &service_user, &error); + if (ret < 0) { + e_error(rcpt->event, "Failed to lookup user %s: %s", + username, error); + smtp_server_recipient_reply(rcpt, 451, "4.3.0", + "Temporary internal error"); + return -1; + } + if (ret == 0) { + smtp_server_recipient_reply(rcpt, 550, "5.1.1", + "User doesn't exist: %s", + username); + return -1; + } + + if (client->local == NULL) + client->local = lmtp_local_init(client); + + llrcpt = p_new(rcpt->pool, struct lmtp_local_recipient, 1); + llrcpt->rcpt = lrcpt; + llrcpt->detail = p_strdup(rcpt->pool, detail); + llrcpt->service_user = service_user; + + lrcpt->type = LMTP_RECIPIENT_TYPE_LOCAL; + lrcpt->backend_context = llrcpt; + + smtp_server_recipient_add_hook( + rcpt, SMTP_SERVER_RECIPIENT_HOOK_DESTROY, + lmtp_local_rcpt_destroy, llrcpt); + smtp_server_recipient_add_hook( + rcpt, SMTP_SERVER_RECIPIENT_HOOK_APPROVED, + lmtp_local_rcpt_approved, llrcpt); + + if (client->lmtp_set->lmtp_user_concurrency_limit == 0) { + (void)lmtp_local_rcpt_anvil_finish(llrcpt); + } else { + /* NOTE: username may change as the result of the userdb + lookup. Look up the new one via service_user. */ + const struct mail_storage_service_input *input = + mail_storage_service_user_get_input(llrcpt->service_user); + const char *query = t_strconcat("LOOKUP\t", + master_service_get_name(master_service), + "/", str_tabescape(input->username), NULL); + llrcpt->anvil_query = anvil_client_query(anvil, query, + lmtp_local_rcpt_anvil_cb, llrcpt); + return 0; + } + + return 1; +} + +/* + * DATA command + */ + +void lmtp_local_add_headers(struct lmtp_local *local, + struct smtp_server_transaction *trans, + string_t *headers) +{ + struct client *client = local->client; + const struct lmtp_settings *lmtp_set = client->lmtp_set; + struct lmtp_local_recipient *const *llrcpts; + const struct smtp_address *rcpt_to = NULL; + unsigned int count; + + str_printfa(headers, "Return-Path: <%s>\r\n", + smtp_address_encode(trans->mail_from)); + + llrcpts = array_get(&local->rcpt_to, &count); + if (count == 1) { + struct smtp_server_recipient *rcpt = llrcpts[0]->rcpt->rcpt; + + switch (lmtp_set->parsed_lmtp_hdr_delivery_address) { + case LMTP_HDR_DELIVERY_ADDRESS_NONE: + break; + case LMTP_HDR_DELIVERY_ADDRESS_FINAL: + rcpt_to = rcpt->path; + break; + case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL: + rcpt_to = rcpt->params.orcpt.addr; + break; + } + } + if (rcpt_to != NULL) { + str_printfa(headers, "Delivered-To: %s\r\n", + smtp_address_encode(rcpt_to)); + } +} + +static int +lmtp_local_deliver(struct lmtp_local *local, + struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, + struct smtp_server_transaction *trans, + struct lmtp_local_recipient *llrcpt, + struct mail *src_mail, + struct mail_deliver_session *session) +{ + struct client *client = local->client; + struct lmtp_recipient *lrcpt = llrcpt->rcpt; + struct smtp_server_recipient *rcpt = lrcpt->rcpt; + struct mail_storage_service_user *service_user = llrcpt->service_user; + struct lmtp_local_deliver_context lldctx; + struct mail_user *rcpt_user; + const struct mail_storage_service_input *input; + const struct mail_storage_settings *mail_set; + struct smtp_submit_settings *smtp_set; + struct smtp_proxy_data proxy_data; + struct lda_settings *lda_set; + struct mail_namespace *ns; + struct setting_parser_context *set_parser; + const struct var_expand_table *var_table; + void **sets; + const char *line, *error, *username; + int ret; + + input = mail_storage_service_user_get_input(service_user); + username = t_strdup(input->username); + + mail_set = mail_storage_service_user_get_mail_set(service_user); + set_parser = mail_storage_service_user_get_settings_parser(service_user); + + smtp_server_connection_get_proxy_data + (client->conn, &proxy_data); + if (proxy_data.timeout_secs > 0 && + (mail_set->mail_max_lock_timeout == 0 || + mail_set->mail_max_lock_timeout > proxy_data.timeout_secs)) { + /* set lock timeout waits to be less than when proxy has + advertised that it's going to timeout the connection. + this avoids duplicate deliveries in case the delivery + succeeds after the proxy has already disconnected from us. */ + line = t_strdup_printf("mail_max_lock_timeout=%us", + proxy_data.timeout_secs <= 1 ? 1 : + proxy_data.timeout_secs-1); + if (settings_parse_line(set_parser, line) < 0) + i_unreached(); + } + + i_zero(&lldctx); + lldctx.session_id = lrcpt->session_id; + lldctx.src_mail = src_mail; + lldctx.session = session; + + /* get the timestamp before user is created, since it starts the I/O */ + io_loop_time_refresh(); + lldctx.delivery_time_started = ioloop_timeval; + + client_update_data_state(client, username); + if (mail_storage_service_next(storage_service, service_user, + &rcpt_user, &error) < 0) { + e_error(rcpt->event, "Failed to initialize user: %s", error); + smtp_server_recipient_reply(rcpt, 451, "4.3.0", + "Temporary internal error"); + return -1; + } + local->rcpt_user = rcpt_user; + + sets = mail_storage_service_user_get_set(service_user); + var_table = mail_user_var_expand_table(rcpt_user); + smtp_set = sets[1]; + lda_set = sets[2]; + ret = settings_var_expand( + &smtp_submit_setting_parser_info, + smtp_set, client->pool, var_table, + &error); + if (ret > 0) { + ret = settings_var_expand( + &lda_setting_parser_info, + lda_set, client->pool, var_table, + &error); + } + if (ret <= 0) { + e_error(rcpt->event, "Failed to expand settings: %s", error); + smtp_server_recipient_reply(rcpt, 451, "4.3.0", + "Temporary internal error"); + return -1; + } + + /* Set the log prefix for the user. The default log prefix is + automatically restored later when user context gets deactivated. */ + i_set_failure_prefix("%s", + mail_storage_service_user_get_log_prefix(service_user)); + + lldctx.rcpt_user = rcpt_user; + lldctx.smtp_set = smtp_set; + lldctx.lda_set = lda_set; + + if (*llrcpt->detail == '\0' || + !client->lmtp_set->lmtp_save_to_detail_mailbox) + lldctx.rcpt_default_mailbox = "INBOX"; + else { + ns = mail_namespace_find_inbox(rcpt_user->namespaces); + lldctx.rcpt_default_mailbox = + t_strconcat(ns->prefix, llrcpt->detail, NULL); + } + + ret = client->v.local_deliver(client, lrcpt, cmd, trans, &lldctx); + + lmtp_local_rcpt_anvil_disconnect(llrcpt); + return ret; +} + +static int +lmtp_local_default_do_deliver(struct lmtp_local *local, + struct lmtp_local_recipient *llrcpt, + struct lmtp_local_deliver_context *lldctx, + struct mail_deliver_context *dctx) +{ + struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt; + enum mail_deliver_error error_code; + const char *error; + + if (mail_deliver(dctx, &error_code, &error) == 0) { + if (dctx->dest_mail != NULL) { + i_assert(local->first_saved_mail == NULL); + local->first_saved_mail = dctx->dest_mail; + } + smtp_server_recipient_reply(rcpt, 250, "2.0.0", "%s Saved", + lldctx->session_id); + return 0; + } + + switch (error_code) { + case MAIL_DELIVER_ERROR_NONE: + i_unreached(); + case MAIL_DELIVER_ERROR_TEMPORARY: + smtp_server_recipient_reply(rcpt, 451, "4.2.0", "%s", error); + break; + case MAIL_DELIVER_ERROR_REJECTED: + smtp_server_recipient_reply(rcpt, 552, "5.2.0", "%s", error); + break; + case MAIL_DELIVER_ERROR_NOQUOTA: + lmtp_local_rcpt_reply_overquota(llrcpt, error); + break; + case MAIL_DELIVER_ERROR_INTERNAL: + /* This shouldn't happen */ + smtp_server_recipient_reply(rcpt, 451, "4.3.0", "%s", error); + break; + } + + return -1; +} + +int lmtp_local_default_deliver(struct client *client, + struct lmtp_recipient *lrcpt, + struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, + struct smtp_server_transaction *trans, + struct lmtp_local_deliver_context *lldctx) +{ + struct lmtp_local *local = client->local; + struct lmtp_local_recipient *llrcpt = lrcpt->backend_context; + struct smtp_server_recipient *rcpt = lrcpt->rcpt; + struct smtp_address *rcpt_to = rcpt->path; + struct mail_deliver_input dinput; + struct mail_deliver_context dctx; + struct event *event; + int ret; + + event = event_create(rcpt->event); + event_drop_parent_log_prefixes(event, 3); + + i_zero(&dinput); + dinput.session = lldctx->session; + dinput.set = lldctx->lda_set; + dinput.smtp_set = lldctx->smtp_set; + dinput.session_id = lldctx->session_id; + dinput.event_parent = event; + dinput.src_mail = lldctx->src_mail; + + /* MAIL FROM */ + dinput.mail_from = trans->mail_from; + dinput.mail_params = trans->params; + + /* RCPT TO */ + dinput.rcpt_user = lldctx->rcpt_user; + dinput.rcpt_params = rcpt->params; + if (dinput.rcpt_params.orcpt.addr == NULL && + *dinput.set->lda_original_recipient_header != '\0') { + dinput.rcpt_params.orcpt.addr = + mail_deliver_get_address( + lldctx->src_mail, + dinput.set->lda_original_recipient_header); + } + if (dinput.rcpt_params.orcpt.addr == NULL) + dinput.rcpt_params.orcpt.addr = rcpt_to; + dinput.rcpt_to = rcpt_to; + dinput.rcpt_default_mailbox = lldctx->rcpt_default_mailbox; + + dinput.save_dest_mail = array_count(&trans->rcpt_to) > 1 && + local->first_saved_mail == NULL; + + dinput.session_time_msecs = + timeval_diff_msecs(&client->state.data_end_timeval, + &trans->timestamp); + dinput.delivery_time_started = lldctx->delivery_time_started; + + mail_deliver_init(&dctx, &dinput); + ret = lmtp_local_default_do_deliver(local, llrcpt, lldctx, &dctx); + mail_deliver_deinit(&dctx); + event_unref(&event); + + return ret; +} + +static uid_t +lmtp_local_deliver_to_rcpts(struct lmtp_local *local, + struct smtp_server_cmd_ctx *cmd, + struct smtp_server_transaction *trans, + struct mail_deliver_session *session) +{ + struct client *client = local->client; + uid_t first_uid = (uid_t)-1; + struct mail *src_mail; + struct lmtp_local_recipient *const *llrcpts; + unsigned int count, i; + int ret; + + src_mail = local->raw_mail; + llrcpts = array_get(&local->rcpt_to, &count); + for (i = 0; i < count; i++) { + struct lmtp_local_recipient *llrcpt = llrcpts[i]; + struct smtp_server_recipient *rcpt = llrcpt->rcpt->rcpt; + + if (llrcpt->duplicate != NULL) { + struct smtp_server_recipient *drcpt = + llrcpt->duplicate->rcpt->rcpt; + /* don't deliver more than once to the same recipient */ + smtp_server_reply_submit_duplicate(cmd, rcpt->index, + drcpt->index); + continue; + } + + ret = lmtp_local_deliver(local, cmd, + trans, llrcpt, src_mail, session); + client_update_data_state(client, NULL); + + /* succeeded and mail_user is not saved in first_saved_mail */ + if ((ret == 0 && + (local->first_saved_mail == NULL || + local->first_saved_mail == src_mail)) || + /* failed. try the next one. */ + (ret != 0 && local->rcpt_user != NULL)) { + if (i == (count - 1)) + mail_user_autoexpunge(local->rcpt_user); + mail_storage_service_io_deactivate_user(local->rcpt_user->_service_user); + mail_user_deinit(&local->rcpt_user); + } else if (ret == 0) { + /* use the first saved message to save it elsewhere too. + this might allow hard linking the files. + mail_user is saved in first_saved_mail, + will be unreferenced later on */ + mail_storage_service_io_deactivate_user(local->rcpt_user->_service_user); + local->rcpt_user = NULL; + src_mail = local->first_saved_mail; + first_uid = geteuid(); + i_assert(first_uid != 0); + } else if (local->rcpt_user != NULL) { + mail_storage_service_io_deactivate_user(local->rcpt_user->_service_user); + } + } + return first_uid; +} + +static int +lmtp_local_open_raw_mail(struct lmtp_local *local, + struct smtp_server_transaction *trans, + struct istream *input) +{ + static const char *wanted_headers[] = { + "From", "To", "Message-ID", "Subject", "Return-Path", + NULL + }; + struct client *client = local->client; + struct mailbox *box; + struct mailbox_transaction_context *mtrans; + struct mailbox_header_lookup_ctx *headers_ctx; + enum mail_error error; + + if (raw_mailbox_alloc_stream(client->raw_mail_user, input, + (time_t)-1, smtp_address_encode(trans->mail_from), + &box) < 0) { + e_error(client->event, "Can't open delivery mail as raw: %s", + mailbox_get_last_internal_error(box, &error)); + mailbox_free(&box); + lmtp_local_rcpt_fail_all(local, 451, "4.3.0", + "Temporary internal error"); + return -1; + } + + mtrans = mailbox_transaction_begin(box, 0, __func__); + + headers_ctx = mailbox_header_lookup_init(box, wanted_headers); + local->raw_mail = mail_alloc(mtrans, 0, headers_ctx); + mailbox_header_lookup_unref(&headers_ctx); + mail_set_seq(local->raw_mail, 1); + return 0; +} + +void lmtp_local_data(struct client *client, + struct smtp_server_cmd_ctx *cmd, + struct smtp_server_transaction *trans, + struct istream *input) +{ + struct lmtp_local *local = client->local; + struct mail_deliver_session *session; + uid_t old_uid, first_uid; + + if (lmtp_local_open_raw_mail(local, trans, input) < 0) + return; + + session = mail_deliver_session_init(); + old_uid = geteuid(); + first_uid = lmtp_local_deliver_to_rcpts(local, cmd, trans, session); + mail_deliver_session_deinit(&session); + + if (local->first_saved_mail != NULL) { + struct mail *mail = local->first_saved_mail; + struct mailbox_transaction_context *trans = mail->transaction; + struct mailbox *box = trans->box; + struct mail_user *user = box->storage->user; + + /* just in case these functions are going to write anything, + change uid back to user's own one */ + if (first_uid != old_uid) { + if (seteuid(0) < 0) + i_fatal("seteuid(0) failed: %m"); + if (seteuid(first_uid) < 0) + i_fatal("seteuid() failed: %m"); + } + + mail_storage_service_io_activate_user(user->_service_user); + mail_free(&mail); + mailbox_transaction_rollback(&trans); + mailbox_free(&box); + mail_user_autoexpunge(user); + mail_storage_service_io_deactivate_user(user->_service_user); + mail_user_deinit(&user); + } + + if (old_uid == 0) { + /* switch back to running as root, since that's what we're + practically doing anyway. it's also important in case we + lose e.g. config connection and need to reconnect to it. */ + if (seteuid(0) < 0) + i_fatal("seteuid(0) failed: %m"); + /* enable core dumping again. we need to chdir also to + root-owned directory to get core dumps. */ + restrict_access_allow_coredumps(TRUE); + if (chdir(base_dir) < 0) { + e_error(client->event, + "chdir(%s) failed: %m", base_dir); + } + } +} |