commands.c revision bf7dc750b95039981c0e9d728f313d50cf38a156
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint cmd_lhlo(struct client *client, const char *args)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* domain / address-literal */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args),
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250-%s", client->my_domain);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (master_service_ssl_is_enabled(master_service) &&
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi client_send_line(client, "250-XCLIENT ADDR PORT TTL TIMEOUT");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "250-ENHANCEDSTATUSCODES");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *error;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "443 5.5.1 TLS is already active.\r\n");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "454 4.7.0 Internal error, TLS not available.\r\n");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "220 2.0.0 Begin TLS negotiation now.\r\n");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ssl_iostream_handshake(client->ssl_iostream) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic int parse_address(const char *str, const char **address_r,
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi const char **rest_r)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *start;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* "quoted-string"@domain */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic const char *
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomiparse_xtext(struct client *client, const char *value)
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi const char *p;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi unsigned int i;
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi hexchar = ASCII "+" immediately followed by two upper case
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi hexadecimal digits
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (value[i] == '+' && value[i+1] != '\0' && value[i+2] != '\0') {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi str_append_c(str, hex2dec((const void *)(value+i+1), 2));
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void lmtp_anvil_init(void)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *path = t_strdup_printf("%s/anvil", base_dir);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint cmd_mail(struct client *client, const char *args)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "503 5.5.1 MAIL already given");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "501 5.5.4 Invalid parameters");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "501 5.5.4 Unsupported options");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->state.mail_from = p_strdup(client->state_pool, addr);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p_array_init(&client->state.rcpt_to, client->state_pool, 64);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_state_set(client, "MAIL FROM", client->state.mail_from);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* connect to anvil before dropping privileges */
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomiclient_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (str_to_uint(value, &set->timeout_msecs) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("proxy: Invalid proxy_timeout value %s", value);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* changing the username */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* just ignore it */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiclient_proxy_is_ourself(const struct client *client,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic const char *
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiaddress_add_detail(const char *username, char delim_c,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *detail)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *domain;
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi return t_strconcat(username, delim, detail, NULL);
66761ea6a8f00006f28743b4bd2f5339f78c10e0Timo Sirainen return t_strconcat(username, delim, detail, domain, NULL);
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomistatic bool client_proxy_rcpt(struct client *client, const char *address,
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi const char *username, const char *detail, char delim,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *args, *const *fields, *errstr, *orig_username = username;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi mail_storage_service_init_settings(storage_service, &input);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi info.service = master_service_get_name(master_service);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool = pool_alloconly_create("auth lookup", 1024);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi auth_conn = mail_storage_service_get_auth_conn(storage_service);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi ret = auth_master_pass_lookup(auth_conn, username, &info,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ret <= 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* user not found from passdb. try userdb also. */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (!client_proxy_rcpt_parse_fields(&set, fields, &username)) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* not proxying this user */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* username changed. change the address as well */
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi address = address_add_detail(username, delim, detail);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi } else if (client_proxy_is_ourself(client, &set)) {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi i_error("Proxying to <%s> loops to itself", username);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("Proxying to <%s> appears to be looping (TTL=0)",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "Proxying appears to be looping (TTL=0)",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi "Can't handle mixed proxy/non-proxy destinations",
17541ea25593c656060199715051db2c1eef221dAki Tuomi proxy_set.dns_client_socket_path = dns_client_socket_path;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client->proxy = lmtp_proxy_init(&proxy_set, client->output);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi lmtp_proxy_mail_from(client->proxy, t_strdup_printf(
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi if (lmtp_proxy_add_rcpt(client->proxy, address, &set) < 0)
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi client_send_line(client, ERRSTR_TEMP_REMOTE_FAILURE);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic const char *lmtp_unescape_address(const char *name)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p;
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi /* quoted-string local-part. drop the quotes unless there's a
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi '@' character inside or there's an error. */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p == '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p == '\\') {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*p == '@')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void rcpt_address_parse(struct client *client, const char *address,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char **detail_r)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *p, *domain;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (*client->unexpanded_lda_set->recipient_delimiter == '\0')
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* first character that matches the recipient_delimiter */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi idx = strcspn(address, client->unexpanded_lda_set->recipient_delimiter);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi if (p != NULL && (domain == NULL || p < domain)) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* user+detail@domain */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi *username_r = t_strconcat(*username_r, domain, NULL);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistatic void lmtp_address_translate(struct client *client, const char **address)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *transpos = client->lmtp_set->lmtp_address_translate;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* check that string matches up to the first '%' */
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi /* find where the next string starts */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const struct mail_recipient *rcpt, const char *error)
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi mail_storage_service_user_get_set(rcpt->service_user)[1];
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi client_send_line(client, "%s <%s> %s", lda_set->quota_full_tempfail ?
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi const char *errstr;
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek ret = mail_storage_service_next(storage_service,
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi i_error("Failed to initialize user %s: %s", rcpt->address, errstr);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi i_error("mailbox_get_status(%s, STATUS_CHECK_OVER_QUOTA) "
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi "failed: %s",
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomistatic bool cmd_rcpt_finish(struct client *client, struct mail_recipient *rcpt)
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) {
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi mail_storage_service_user_unref(&rcpt->service_user);
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainenstatic void rcpt_anvil_lookup_callback(const char *reply, void *context)
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen unsigned int parallel_count = 0;
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen /* lookup failed */
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi } else if (str_to_uint(reply, ¶llel_count) < 0) {
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen "Too many concurrent deliveries for user",
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen mail_storage_service_user_unref(&rcpt->service_user);
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi input = mail_storage_service_user_get_input(rcpt->service_user);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi master_service_anvil_send(master_service, t_strconcat(
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi "CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
004be038dfe290f71e3d4a4b14d88673e8b55fb2Timo Sirainenint cmd_rcpt(struct client *client, const char *args)
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi const char *const *argv;
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_send_line(client, "503 5.5.1 MAIL needed first");
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi client_send_line(client, "501 5.5.4 Invalid parameters");
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt = p_new(client->state_pool, struct mail_recipient, 1);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi client_send_line(client, "501 5.5.4 Unsupported options");
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi rcpt_address_parse(client, address, &username, &delim, &detail);
if (ret < 0) {
if (ret == 0) {
address);
return FALSE;
return TRUE;
void **sets;
int ret;
i_unreached();
ret = 0;
return ret;
unsigned int count;
int ret;
if (ret == 0)
return TRUE;
return FALSE;
return cinput;
static const char *wanted_headers[] = {
&box) < 0) {
if (seteuid(0) < 0)
if (old_uid == 0) {
if (seteuid(0) < 0)
void **sets;
&rcpt_to))
int fd;
const unsigned char *data;
if (ret == 0)
const char *const *tmp;
if (!args_ok) {
if (remote_port != 0)