commands.c revision eb318ea05532d2e54ed3bfc89bc15dcf1adae838
/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "str.h"
#include "strescape.h"
#include "hostpid.h"
#include "istream.h"
#include "istream-concat.h"
#include "ostream.h"
#include "istream-dot.h"
#include "safe-mkstemp.h"
#include "hex-dec.h"
#include "time-util.h"
#include "var-expand.h"
#include "restrict-access.h"
#include "settings-parser.h"
#include "anvil-client.h"
#include "master-service.h"
#include "master-service-ssl.h"
#include "iostream-ssl.h"
#include "rfc822-parser.h"
#include "message-date.h"
#include "auth-master.h"
#include "mail-storage-service.h"
#include "index/raw/raw-storage.h"
#include "lda-settings.h"
#include "lmtp-settings.h"
#include "mail-namespace.h"
#include "mail-deliver.h"
#include "main.h"
#include "client.h"
#include "commands.h"
#include "lmtp-proxy.h"
#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
#define ERRSTR_TEMP_USERDB_FAIL \
ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
{
struct rfc822_parser_context parser;
const char *p;
int ret = 0;
if (*args == '\0') {
return 0;
}
/* domain / address-literal */
NULL);
if (*args != '[')
else {
if (*p == '\\' || *p == '[')
break;
}
if (strcmp(p, "]") != 0)
ret = -1;
}
if (ret < 0) {
str_truncate(domain, 0);
}
if (client_is_trusted(client))
return 0;
}
{
const char *error;
"443 5.5.1 TLS is already active.\r\n");
return 0;
}
"454 4.7.0 Internal error, TLS not available.\r\n");
return 0;
}
"220 2.0.0 Begin TLS negotiation now.\r\n");
return -1;
}
return 0;
}
const char **rest_r)
{
const char *start;
if (*str++ != '<')
return -1;
if (*str == '"') {
/* "quoted-string"@domain */
if (*str == '\\')
str++;
if (*str == '\0')
return -1;
}
str++;
}
return -1;
}
if (*str++ != '>')
return -1;
if (*str == ' ')
str++;
else if (*str != '\0')
return -1;
return 0;
}
static const char *
{
const char *p;
unsigned int i;
if (p == NULL)
/*
hexchar = ASCII "+" immediately followed by two upper case
hexadecimal digits
*/
for (i = 0; value[i] != '\0'; i++) {
i += 2;
} else {
}
}
}
static void lmtp_anvil_init(void)
{
}
}
{
return 0;
}
return 0;
}
else {
"501 5.5.4 Unsupported options");
return 0;
}
}
/* connect to anvil before dropping privileges */
}
return 0;
}
static bool
{
if (p == NULL) {
value = "";
} else {
value = p + 1;
}
return FALSE;
}
return FALSE;
}
if (!port_set)
if (!port_set)
} else {
return FALSE;
}
/* changing the username */
} else {
/* just ignore it */
}
}
i_error("proxy: host not given");
return FALSE;
}
return proxying;
}
static bool
const struct lmtp_proxy_rcpt_settings *set)
{
return FALSE;
return FALSE;
return FALSE;
return TRUE;
}
static const char *
const char *detail)
{
const char *domain;
else {
}
}
const struct lmtp_recipient_params *params)
{
struct auth_master_connection *auth_conn;
struct lmtp_proxy_rcpt_settings set;
struct auth_user_info info;
struct mail_storage_service_input input;
int ret;
if (ret <= 0) {
pool_unref(&pool);
if (ret < 0) {
return TRUE;
} else {
/* user not found from passdb. try userdb also. */
return FALSE;
}
}
/* not proxying this user */
pool_unref(&pool);
return FALSE;
}
/* username changed. change the address as well */
if (*detail == '\0')
else
"Proxying loops to itself", address);
pool_unref(&pool);
return TRUE;
}
i_error("Proxying to <%s> appears to be looping (TTL=0)",
username);
"Proxying appears to be looping (TTL=0)",
username);
pool_unref(&pool);
return TRUE;
}
address);
pool_unref(&pool);
return TRUE;
}
struct lmtp_proxy_settings proxy_set;
args = " BODY=8BITMIME";
args = " BODY=7BIT";
else
args = "";
}
else
pool_unref(&pool);
return TRUE;
}
static const char *lmtp_unescape_address(const char *name)
{
const char *p;
if (*name != '"')
return name;
/* quoted-string local-part. drop the quotes unless there's a
'@' character inside or there's an error. */
if (*p == '\0')
return name;
if (*p == '\\') {
if (p[1] == '\0') {
/* error */
return name;
}
p++;
}
if (*p == '@')
return name;
str_append_c(str, *p);
}
p++;
if (*p != '@' && *p != '\0')
return name;
str_append(str, p);
}
const char **username_r, char *delim_r,
const char **detail_r)
{
const char *p, *domain;
*username_r = address;
*detail_r = "";
return;
/* first character that matches the recipient_delimiter */
*delim_r = *p;
/* user+detail@domain */
*detail_r = p+1;
else {
}
}
}
{
if (*transpos == '\0')
return;
/* check that string matches up to the first '%' */
if (p == NULL)
else
return;
while (*transpos != '\0') {
switch (transpos[1]) {
case 'n':
case 'u':
break;
case 'd':
break;
default:
return;
}
transpos += 2;
/* find where the next string starts */
if (*transpos == '\0') {
break;
}
if (p == NULL)
else
if (p == NULL)
return;
}
}
static void
{
struct lda_settings *lda_set =
}
static int
const struct mail_recipient *rcpt)
{
struct mail_namespace *ns;
struct mailbox_status status;
const char *errstr;
enum mail_error error;
int ret;
return 0;
if (ret < 0) {
return -1;
}
if (ret < 0) {
if (error == MAIL_ERROR_NOQUOTA) {
ret = 1;
}
}
mailbox_free(&box);
return ret;
}
{
int ret;
if (ret < 0) {
}
return FALSE;
}
return TRUE;
}
{
const struct mail_storage_service_input *input;
unsigned int parallel_count = 0;
/* lookup failed */
}
"Too many concurrent deliveries for user",
}
}
{
struct mail_recipient *rcpt;
struct mail_storage_service_input input;
const char *const *argv;
char delim = '\0';
int ret = 0;
return 0;
}
return 0;
}
} else {
return 0;
}
}
return 0;
}
/* Use a unique session_id for each mail delivery. This is especially
important for stats process to not see duplicate sessions. */
else {
rcpt->session_id =
}
if (ret < 0) {
return 0;
}
if (ret == 0) {
"550 5.1.1 <%s> User doesn't exist: %s",
return 0;
}
/* NOTE: if this restriction is ever removed, we'll also need
to send different message bodies to local and proxy
(with and without Return-Path: header) */
address);
return 0;
}
return 0;
} 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 =
/* stop processing further commands while anvil query is
pending */
}
}
{
/* don't log the (state name) for successful QUITs */
return -1;
}
{
return 0;
}
{
return 0;
}
{
return 0;
}
{
return FALSE;
/* FIXME: we should verify the address further */
return TRUE;
}
static int
{
struct mail_deliver_context dctx;
struct mail_storage *storage;
const struct mail_storage_service_input *input;
const struct mail_storage_settings *mail_set;
struct lda_settings *lda_set;
struct mail_namespace *ns;
struct setting_parser_context *set_parser;
struct timeval delivery_time_started;
void **sets;
enum mail_error mail_error;
int ret;
if (client->proxy_timeout_secs > 0 &&
(mail_set->mail_max_lock_timeout == 0 ||
/* 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. */
i_unreached();
}
/* get the timestamp before user is created, since it starts the I/O */
return -1;
}
return -1;
}
i_error("Failed to expand mail_log_prefix=%s: %s",
return -1;
}
/* used ORCPT */
}
else {
}
}
ret = 0;
ret = -1;
if (mail_error == MAIL_ERROR_NOQUOTA) {
} else {
}
ret = -1;
} else {
/* This shouldn't happen */
i_error("BUG: Saving failed to unknown storage");
ret = -1;
}
return ret;
}
struct mail_deliver_session *session)
{
struct mail_recipient *const *rcpts;
unsigned int count;
int ret;
if (ret == 0)
return TRUE;
/* failed. try the next one. */
}
return FALSE;
}
{
struct mail_recipient *const *rcptp;
}
}
{
} else {
}
i_stream_unref(&inputs[0]);
return cinput;
}
{
static const char *wanted_headers[] = {
"From", "To", "Message-ID", "Subject", "Return-Path",
};
struct mailbox_transaction_context *trans;
struct mailbox_header_lookup_ctx *headers_ctx;
enum mail_error error;
&box) < 0) {
i_error("Can't open delivery mail as raw: %s",
mailbox_free(&box);
return -1;
}
return 0;
}
static void
{
struct mail_deliver_session *session;
return;
else {
/* use the first saved message to save it elsewhere too.
this might allow hard linking the files. */
}
}
/* just in case these functions are going to write anything,
change uid back to user's own one */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
i_fatal("seteuid() failed: %m");
}
mailbox_free(&box);
}
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. */
}
}
{
}
static void client_proxy_finish(void *context)
{
}
{
void **sets;
const struct lmtp_settings *lmtp_set;
struct mail_recipient *const *rcptp =
switch (lmtp_set->parsed_lmtp_hdr_delivery_address) {
break;
break;
&rcpt_to))
break;
}
}
/* don't set Return-Path when proxying so it won't get added twice */
}
if (host[0] != '\0')
}
}
{
} else {
}
}
{
int fd;
/* continue writing to file */
return -1;
return 0;
}
/* move everything to a temporary file. */
if (fd == -1) {
return -1;
}
/* we just want the fd, unlink it */
/* shouldn't happen.. */
i_close_fd(&fd);
return -1;
}
return -1;
}
return 0;
}
static int
{
return 0;
} else {
}
}
{
const unsigned char *data;
"Temporary internal failure");
return;
}
}
if (ret == 0)
return;
/* client probably disconnected */
return;
}
/* the ending "." line was seen. begin saving the mail. */
}
{
if (client_input_read(client) < 0)
return;
}
{
return 0;
}
return 0;
}
/* send the DATA reply immediately before we start handling any data */
return -1;
}
{
const char *const *tmp;
in_port_t remote_port = 0;
if (!client_is_trusted(client)) {
return 0;
}
}
}
if (!args_ok) {
return 0;
}
/* args ok, set them and reset the state */
if (remote_port != 0)
return 0;
}