main.c revision b1c85a1f889a5e71f491e320bdac95df3c9fe550
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen/* After buffer grows larger than this, create a temporary file to /tmp
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen where to read the mail. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic const char *wanted_headers[] = {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen "From", "To", "Message-ID", "Subject", "Return-Path",
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainenstatic const char *escape_local_part(const char *local_part)
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen const char *p;
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen /* if local_part isn't dot-atom-text, we need to return quoted-string
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen dot-atom-text = 1*atext *("." 1*atext) */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen local_part = t_strdup_printf("\"%s\"", str_escape(local_part));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic const char *address_sanitize(const char *address)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen pool = pool_alloconly_create("address sanitizer", 256);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen addr = message_address_parse(pool, (const unsigned char *)address,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = t_strdup_printf("%s@%s", mailbox, addr->domain);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic int seekable_fd_callback(const char **path_r, void *context)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen mail_user_set_get_temp_prefix(path, ctx->dest_user->set);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_error("safe_mkstemp(%s) failed: %m", str_c(path));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we just want the fd, unlink it */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* shouldn't happen.. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic struct istream *
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainencreate_raw_stream(struct mail_deliver_context *ctx,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream *input, *input2, *input_list[2];
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const unsigned char *data;
56d1345c43bbd28c36b7faa85e4163bd9e874290Timo Sirainen /* If input begins with a From-line, drop it */
7a727b88fdfccf74466041c17ebb34c1da663567Timo Sirainen ret = i_stream_read_bytes(input, &data, &size, 5);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (ret > 0 && memcmp(data, "From ", 5) == 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* skip until the first LF */
ebcf7d6c9222f2c96053516e0c90994bff62dd55Timo Sirainen while (i_stream_read_more(input, &data, &size) > 0) {
ebcf7d6c9222f2c96053516e0c90994bff62dd55Timo Sirainen for (i = 0; i < size; i++) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (sender != NULL && ctx->src_envelope_sender == NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* use the envelope sender from From_-line, but only if it
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen hasn't been specified with -f already. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ctx->src_envelope_sender = p_strdup(ctx->pool, sender);
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen input2 = i_stream_create_limit(input, (uoff_t)-1);
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen input = i_stream_create_seekable(input_list, MAIL_MAX_MEMORY_BUFFER,
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainenstatic struct mail *
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainenlda_raw_mail_open(struct mail_deliver_context *ctx, const char *path)
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen struct mailbox_header_lookup_ctx *headers_ctx;
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen sets = master_service_settings_get_others(master_service);
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen raw_storage_create_from_set(ctx->dest_user->set_info, sets[0]);
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen envelope_sender = ctx->src_envelope_sender != NULL ?
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen ctx->src_envelope_sender : DEFAULT_ENVELOPE_SENDER;
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen ret = raw_mailbox_alloc_stream(raw_mail_user, input, mtime,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = raw_mailbox_alloc_path(raw_mail_user, path, (time_t)-1,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_fatal("Can't open delivery mail as raw: %s",
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek mailbox_header_lookup_unref(&headers_ctx);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenlda_set_dest_addr(struct mail_deliver_context *ctx, const char *user,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen *ctx->set->lda_original_recipient_header != '\0') {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ctx->dest_addr = mail_deliver_get_address(ctx->src_mail,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ctx->set->lda_original_recipient_header, " header", NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ctx->dest_addr = strchr(user, '@') != NULL ? user :
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen t_strconcat(user, "@", ctx->set->hostname, NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_debug("Destination address: %s (source: %s)",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we want all our exit codes to be sysexits.h compatible.
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if we failed because of a logging related error, we most likely
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen aren't writing to stderr, so try writing there to give some kind of
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen a clue what's wrong. FATAL_LOGOPEN failure already wrote to
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stderr, so don't duplicate it. */
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainenstatic void print_help(void)
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen"Usage: dovecot-lda [-c <config file>] [-a <address>] [-d <username>] [-p <path>]\n"
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen" [-f <envelope sender>] [-m <mailbox>] [-e] [-k]\n");
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen const struct setting_parser_info *set_roots[] = {
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen enum mail_storage_service_flags service_flags = 0;
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen struct mail_storage_service_ctx *storage_service;
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen struct mail_storage_service_user *service_user;
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen struct mail_storage_service_input service_input;
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen const char *user_source = "", *destaddr_source = "";
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen if (getuid() != geteuid() && geteuid() == 0) {
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen /* running setuid - don't allow this if the binary is
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen executable by anyone */
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen } else if ((st.st_mode & 1) != 0 && (st.st_mode & 04000) != 0) {
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen fprintf(stderr, "%s must not be both world-executable "
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen "and setuid-root. This allows root exploits. "
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen "See http://wiki2.dovecot.org/LDA#multipleuids\n",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_set_failure_exit_callback(failure_exit_callback);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ctx.timeout_secs = LDA_SUBMISSION_TIMEOUT_SECS;
39a97e88db1d6626469f905085b787c268502153Timo Sirainen while ((c = master_getopt(master_service)) > 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* original recipient address */
35f3b7e05afecacd0332c210c6e253911c2813d8Timo Sirainen /* destination user */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
aec1d6f3cdce8deed8b5d718fe4031ef7432cec1Timo Sirainen /* envelope sender address */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* destination mailbox.
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen Ignore -m "". This allows doing -m ${extension}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen in Postfix to handle user+mailbox */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* input path */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* final recipient address */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if ((service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (process_euid != 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we're non-root. get our username and possibly our home. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* no need for a pw lookup */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else if ((ret = i_getpwuid(process_euid, &pw)) > 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen env_put(t_strconcat("HOME=", pw.pw_dir, NULL));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen user_source = "passwd lookup for process euid";
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen } else if (ret < 0) {
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen /* temporary failure */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen "Couldn't lookup our username (uid=%s)",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen "destination user parameter (-d user) not given");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT |
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen storage_service = mail_storage_service_init(master_service, set_roots,
aec1d6f3cdce8deed8b5d718fe4031ef7432cec1Timo Sirainen /* set before looking up the user (or ideally we'd do this between
aec1d6f3cdce8deed8b5d718fe4031ef7432cec1Timo Sirainen _lookup() and _next(), but don't bother) */
aec1d6f3cdce8deed8b5d718fe4031ef7432cec1Timo Sirainen ret = mail_storage_service_lookup_next(storage_service, &service_input,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen lda_set = mail_storage_service_user_get_set(service_user)[1];
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (settings_var_expand(&lda_setting_parser_info, lda_set,
84cccc6b54d8f92fdee75fe96c63bb67b097eeb3Timo Sirainen i_fatal("Failed to expand settings: %s", errstr);
84cccc6b54d8f92fdee75fe96c63bb67b097eeb3Timo Sirainen if (ctx.dest_user->mail_debug && *user_source != '\0') {
84cccc6b54d8f92fdee75fe96c63bb67b097eeb3Timo Sirainen i_debug("userdb lookup skipped, username taken from %s",
934f04e8a86c14a425f82ec7c74c169492093f98Timo Sirainen lda_set_dest_addr(&ctx, user, destaddr_source);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen errstr = mail_storage_get_last_error(storage, &error);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* This shouldn't happen */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_error("BUG: Saving failed to unknown storage");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* write to stderr also for tempfails so that MTA
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen can log the reason if it wants to. */
934f04e8a86c14a425f82ec7c74c169492093f98Timo Sirainen /* Saving to INBOX should always work unless
934f04e8a86c14a425f82ec7c74c169492093f98Timo Sirainen we're over quota. If it didn't, it's probably a
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen configuration problem. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we'll have to reply with permanent failure */
a67b39aac630b8d53c1afeec6abdc32ba91899c5Timo Sirainen ret = mail_send_rejection(&ctx, user, errstr);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* ok, rejection sent */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen mail_storage_service_user_free(&service_user);