diff options
author | Eric Faurot <eric@cvs.openbsd.org> | 2014-02-04 15:44:07 +0000 |
---|---|---|
committer | Eric Faurot <eric@cvs.openbsd.org> | 2014-02-04 15:44:07 +0000 |
commit | 5e47a3d0175f739592e7b34ff0c015080411622f (patch) | |
tree | 735ab917098eacfabf96ca7885c6be3a5c0e908b | |
parent | 031ed87ff6982c47a192337c6ac5fd231f8ea68b (diff) |
Add support for DSN and Enhanced Status Code
-rw-r--r-- | usr.sbin/smtpd/bounce.c | 51 | ||||
-rw-r--r-- | usr.sbin/smtpd/enqueue.c | 35 | ||||
-rw-r--r-- | usr.sbin/smtpd/envelope.c | 114 | ||||
-rw-r--r-- | usr.sbin/smtpd/esc.c | 137 | ||||
-rw-r--r-- | usr.sbin/smtpd/mda.c | 31 | ||||
-rw-r--r-- | usr.sbin/smtpd/mta.c | 26 | ||||
-rw-r--r-- | usr.sbin/smtpd/mta_session.c | 76 | ||||
-rw-r--r-- | usr.sbin/smtpd/queue.c | 48 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtp_session.c | 260 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd-api.h | 81 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 40 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd/Makefile | 4 |
12 files changed, 800 insertions, 103 deletions
diff --git a/usr.sbin/smtpd/bounce.c b/usr.sbin/smtpd/bounce.c index ded25af80b2..d9decd60734 100644 --- a/usr.sbin/smtpd/bounce.c +++ b/usr.sbin/smtpd/bounce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bounce.c,v 1.61 2014/02/04 14:56:03 eric Exp $ */ +/* $OpenBSD: bounce.c,v 1.62 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> @@ -95,6 +95,7 @@ static void bounce_status(struct bounce_session *, const char *, ...); static void bounce_io(struct io *, int); static void bounce_timeout(int, short, void *); static void bounce_free(struct bounce_session *); +static const char *bounce_strtype(enum bounce_type); static struct tree wait_fd; static struct bounce_message_tree messages; @@ -328,6 +329,12 @@ const char *notice_warning2 = " The message is kept in the queue for up to %s.\n" " You DO NOT NEED to re-send the message to these recipients.\n\n"; +const char *notice_success = + " Your message was successfully delivered to these recipients.\n\n"; + +const char *notice_relay = + " Your message was relayed to these recipients.\n\n"; + static int bounce_next_message(struct bounce_session *s) { @@ -420,18 +427,29 @@ bounce_next(struct bounce_session *s) "\n" NOTICE_INTRO "\n", - (s->msg->bounce.type == B_ERROR) ? "error" : "warning", + bounce_strtype(s->msg->bounce.type), s->smtpname, s->msg->to, time_to_text(time(NULL))); - if (s->msg->bounce.type == B_ERROR) + switch (s->msg->bounce.type) { + case B_ERROR: iobuf_xfqueue(&s->iobuf, "bounce_next: BODY", notice_error); - else + break; + case B_WARNING: iobuf_xfqueue(&s->iobuf, "bounce_next: BODY", notice_warning, bounce_duration(s->msg->bounce.delay)); + break; + case B_DSN: + iobuf_xfqueue(&s->iobuf, "bounce_next: BODY", + s->msg->bounce.mta_without_dsn ? + notice_relay : notice_success); + break; + default: + log_warn("warn: bounce: unknown bounce_type"); + } TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { iobuf_xfqueue(&s->iobuf, @@ -463,6 +481,15 @@ bounce_next(struct bounce_session *s) line = fgetln(s->msgfp, &len); if (line == NULL) break; + if (len == 1 && line[0] == '\n' && /* end of headers */ + s->msg->bounce.type == B_DSN && + s->msg->bounce.dsn_ret == DSN_RETHDRS) { + fclose(s->msgfp); + s->msgfp = NULL; + bounce_send(s, "."); + s->state = BOUNCE_DATA_END; + return (0); + } line[len - 1] = '\0'; iobuf_xfqueue(&s->iobuf, "bounce_next: DATA_MESSAGE", "%s%s\n", @@ -693,5 +720,21 @@ bounce_message_cmp(const struct bounce_message *a, return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce)); } +static const char * +bounce_strtype(enum bounce_type t) +{ + switch (t) { + case B_ERROR: + return ("error"); + case B_WARNING: + return ("warning"); + case B_DSN: + return ("dsn"); + default: + log_warn("warn: bounce: unknown bounce_type"); + return (""); + } +} + SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry, bounce_message_cmp); diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c index fd36b8d161c..7b401bad777 100644 --- a/usr.sbin/smtpd/enqueue.c +++ b/usr.sbin/smtpd/enqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: enqueue.c,v 1.74 2013/12/26 17:25:32 eric Exp $ */ +/* $OpenBSD: enqueue.c,v 1.75 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> @@ -107,6 +107,9 @@ struct { char *from; char *fromname; char **rcpts; + char *dsn_notify; + char *dsn_ret; + char *dsn_envid; int rcpt_cnt; int need_linesplit; int saw_date; @@ -163,9 +166,9 @@ enqueue(int argc, char *argv[]) char *fake_from = NULL, *buf; struct passwd *pw; FILE *fp, *fout; + size_t len, envid_sz = 0; int fd; char sfn[] = "/tmp/smtpd.XXXXXXXXXX"; - size_t len; char *line; int dotted; int inheaders = 0; @@ -179,7 +182,7 @@ enqueue(int argc, char *argv[]) save_argv = argv; while ((ch = getopt(argc, argv, - "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvx")) != -1) { + "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvV:x")) != -1) { switch (ch) { case 'f': fake_from = optarg; @@ -187,12 +190,21 @@ enqueue(int argc, char *argv[]) case 'F': msg.fromname = optarg; break; + case 'N': + msg.dsn_notify = optarg; + break; + case 'R': + msg.dsn_ret = optarg; + break; case 't': tflag = 1; break; case 'v': verbose = 1; break; + case 'V': + msg.dsn_envid = optarg; + break; /* all remaining: ignored, sendmail compat */ case 'A': case 'B': @@ -202,10 +214,8 @@ enqueue(int argc, char *argv[]) case 'i': case 'L': case 'm': - case 'N': /* XXX: DSN */ case 'o': case 'p': - case 'R': case 'x': break; case 'q': @@ -275,11 +285,22 @@ enqueue(int argc, char *argv[]) send_line(fout, verbose, "EHLO localhost\n"); get_responses(fout, 1); - send_line(fout, verbose, "MAIL FROM:<%s>\n", msg.from); + if (msg.dsn_envid != NULL) + envid_sz = strlen(msg.dsn_envid); + + send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n", + msg.from, + msg.dsn_ret ? "RET=" : "", + msg.dsn_ret ? msg.dsn_ret : "", + envid_sz ? "ENVID=" : "", + envid_sz ? msg.dsn_envid : ""); get_responses(fout, 1); for (i = 0; i < msg.rcpt_cnt; i++) { - send_line(fout, verbose, "RCPT TO:<%s>\n", msg.rcpts[i]); + send_line(fout, verbose, "RCPT TO:<%s> %s%s\n", + msg.rcpts[i], + msg.dsn_notify ? "NOTIFY=" : "", + msg.dsn_notify ? msg.dsn_notify : ""); get_responses(fout, 1); } diff --git a/usr.sbin/smtpd/envelope.c b/usr.sbin/smtpd/envelope.c index 4d3e0d071e7..244052bb607 100644 --- a/usr.sbin/smtpd/envelope.c +++ b/usr.sbin/smtpd/envelope.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envelope.c,v 1.27 2014/02/04 13:44:41 eric Exp $ */ +/* $OpenBSD: envelope.c,v 1.28 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -67,6 +67,18 @@ envelope_set_errormsg(struct envelope *e, char *fmt, ...) strlcpy(e->errorline + (sizeof(e->errorline) - 4), "...", 4); } +void +envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class) +{ + e->esc_class = class; +} + +void +envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code) +{ + e->esc_code = code; +} + static int envelope_buffer_to_dict(struct dict *d, const char *ibuf, size_t buflen) { @@ -188,6 +200,12 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) envelope_ascii_dump(ep, &dest, &len, "expire"); envelope_ascii_dump(ep, &dest, &len, "retry"); envelope_ascii_dump(ep, &dest, &len, "flags"); + envelope_ascii_dump(ep, &dest, &len, "dsn-notify"); + envelope_ascii_dump(ep, &dest, &len, "dsn-ret"); + envelope_ascii_dump(ep, &dest, &len, "dsn-envid"); + envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt"); + envelope_ascii_dump(ep, &dest, &len, "esc-class"); + envelope_ascii_dump(ep, &dest, &len, "esc-code"); switch (ep->type) { case D_MDA: @@ -221,6 +239,17 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) } static int +ascii_load_uint8(uint8_t *dest, char *buf) +{ + const char *errstr; + + *dest = strtonum(buf, 0, 0xff, &errstr); + if (errstr) + return 0; + return 1; +} + +static int ascii_load_uint16(uint16_t *dest, char *buf) { const char *errstr; @@ -382,6 +411,20 @@ ascii_load_bounce_type(enum bounce_type *dest, char *buf) *dest = B_ERROR; else if (strcasecmp(buf, "warn") == 0) *dest = B_WARNING; + else if (strcasecmp(buf, "dsn") == 0) + *dest = B_DSN; + else + return 0; + return 1; +} + +static int +ascii_load_dsn_ret(enum dsn_ret *ret, char *buf) +{ + if (strcasecmp(buf, "HDRS") == 0) + *ret = DSN_RETHDRS; + else if (strcasecmp(buf, "FULL") == 0) + *ret = DSN_RETFULL; else return 0; return 1; @@ -500,6 +543,24 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf) if (strcasecmp("version", field) == 0) return ascii_load_uint32(&ep->version, buf); + if (strcasecmp("dsn-notify", field) == 0) + return ascii_load_uint8(&ep->dsn_notify, buf); + + if (strcasecmp("dsn-orcpt", field) == 0) + return ascii_load_mailaddr(&ep->dsn_orcpt, buf); + + if (strcasecmp("dsn-ret", field) == 0) + return ascii_load_dsn_ret(&ep->dsn_ret, buf); + + if (strcasecmp("dsn-envid", field) == 0) + return ascii_load_string(ep->dsn_envid, buf, sizeof(ep->dsn_envid)); + + if (strcasecmp("esc-class", field) == 0) + return ascii_load_uint8(&ep->esc_class, buf); + + if (strcasecmp("esc-code", field) == 0) + return ascii_load_uint8(&ep->esc_code, buf); + return (0); } @@ -524,6 +585,12 @@ err: static int +ascii_dump_uint8(uint8_t src, char *dest, size_t len) +{ + return bsnprintf(dest, len, "%d", src); +} + +static int ascii_dump_uint16(uint16_t src, char *dest, size_t len) { return bsnprintf(dest, len, "%d", src); @@ -667,12 +734,30 @@ ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len) case B_WARNING: p = "warn"; break; + case B_DSN: + p = "dsn"; + break; default: return 0; } return bsnprintf(dest, len, "%s", p); } + +static int +ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len) +{ + size_t cpylen = 0; + + dest[0] = '\0'; + if (flag == DSN_RETFULL) + cpylen = strlcat(dest, "FULL", len); + else if (flag == DSN_RETHDRS) + cpylen = strlcat(dest, "HDRS", len); + + return cpylen < len ? 1 : 0; +} + static int ascii_dump_field(const char *field, const struct envelope *ep, char *buf, size_t len) @@ -785,6 +870,33 @@ ascii_dump_field(const char *field, const struct envelope *ep, if (strcasecmp(field, "version") == 0) return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len); + if (strcasecmp(field, "dsn-notify") == 0) + return ascii_dump_uint8(ep->dsn_notify, buf, len); + + if (strcasecmp(field, "dsn-ret") == 0) + return ascii_dump_dsn_ret(ep->dsn_ret, buf, len); + + if (strcasecmp(field, "dsn-orcpt") == 0) { + if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0]) + return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len); + return 1; + } + + if (strcasecmp(field, "dsn-envid") == 0) + return ascii_dump_string(ep->dsn_envid, buf, len); + + if (strcasecmp(field, "esc-class") == 0) { + if (ep->esc_class) + return ascii_dump_uint8(ep->esc_class, buf, len); + return 1; + } + + if (strcasecmp(field, "esc-code") == 0) { + if (ep->esc_class) + return ascii_dump_uint8(ep->esc_code, buf, len); + return 1; + } + return (0); } diff --git a/usr.sbin/smtpd/esc.c b/usr.sbin/smtpd/esc.c new file mode 100644 index 00000000000..7bccc22d9ab --- /dev/null +++ b/usr.sbin/smtpd/esc.c @@ -0,0 +1,137 @@ +/* $OpenBSD: esc.c,v 1.1 2014/02/04 15:44:05 eric Exp $ */ + +/* + * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> + +#include "smtpd-defines.h" +#include "smtpd-api.h" + +static struct escode { + enum enhanced_status_code code; + const char *description; +} esc[] = { + /* 0.0 */ + { ESC_OTHER_STATUS, "Other/Undefined" }, + + /* 1.x */ + { ESC_OTHER_ADDRESS_STATUS, "Other/Undefined address status" }, + { ESC_BAD_DESTINATION_MAILBOX_ADDRESS, "Bad destination mailbox address" }, + { ESC_BAD_DESTINATION_SYSTEM_ADDRESS, "Bad destination system address" }, + { ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX, "Bad destination mailbox address syntax" }, + { ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS, "Destination mailbox address ambiguous" }, + { ESC_DESTINATION_ADDRESS_VALID, "Destination address valid" }, + { ESC_DESTINATION_MAILBOX_HAS_MOVED, "Destination mailbox has moved, No forwarding address" }, + { ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX, "Bad sender's mailbox address syntax" }, + { ESC_BAD_SENDER_SYSTEM_ADDRESS, "Bad sender's system address syntax" }, + + /* 2.x */ + { ESC_OTHER_MAILBOX_STATUS, "Other/Undefined mailbox status" }, + { ESC_MAILBOX_DISABLED, "Mailbox disabled, not accepting messages" }, + { ESC_MAILBOX_FULL, "Mailbox full" }, + { ESC_MESSAGE_LENGTH_TOO_LARGE, "Message length exceeds administrative limit" }, + { ESC_MAILING_LIST_EXPANSION_PROBLEM, "Mailing list expansion problem" }, + + /* 3.x */ + { ESC_OTHER_MAIL_SYSTEM_STATUS, "Other/Undefined mail system status" }, + { ESC_MAIL_SYSTEM_FULL, "Mail system full" }, + { ESC_SYSTEM_NOT_ACCEPTING_MESSAGES, "System not accepting network messages" }, + { ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES, "System not capable of selected features" }, + { ESC_MESSAGE_TOO_BIG_FOR_SYSTEM, "Message too big for system" }, + { ESC_SYSTEM_INCORRECTLY_CONFIGURED, "System incorrectly configured" }, + + /* 4.x */ + { ESC_OTHER_NETWORK_ROUTING_STATUS, "Other/Undefined network or routing status" }, + { ESC_NO_ANSWER_FROM_HOST, "No answer from host" }, + { ESC_BAD_CONNECTION, "Bad connection" }, + { ESC_DIRECTORY_SERVER_FAILURE, "Directory server failure" }, + { ESC_UNABLE_TO_ROUTE, "Unable to route" }, + { ESC_MAIL_SYSTEM_CONGESTION, "Mail system congestion" }, + { ESC_ROUTING_LOOP_DETECTED, "Routing loop detected" }, + { ESC_DELIVERY_TIME_EXPIRED, "Delivery time expired" }, + + /* 5.x */ + { ESC_OTHER_PROTOCOL_STATUS, "Other/Undefined protocol status" }, + { ESC_INVALID_COMMAND, "Invalid command" }, + { ESC_SYNTAX_ERROR, "Syntax error" }, + { ESC_TOO_MANY_RECIPIENTS, "Too many recipients" }, + { ESC_INVALID_COMMAND_ARGUMENTS, "Invalid command arguments" }, + { ESC_WRONG_PROTOCOL_VERSION, "Wrong protocol version" }, + + /* 6.x */ + { ESC_OTHER_MEDIA_ERROR, "Other/Undefined media error" }, + { ESC_MEDIA_NOT_SUPPORTED, "Media not supported" }, + { ESC_CONVERSION_REQUIRED_AND_PROHIBITED, "Conversion required and prohibited" }, + { ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED, "Conversion required but not supported" }, + { ESC_CONVERSION_WITH_LOSS_PERFORMED, "Conversion with loss performed" }, + { ESC_CONVERSION_FAILED, "Conversion failed" }, + + /* 7.x */ + { ESC_OTHER_SECURITY_STATUS, "Other/Undefined security status" }, + { ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED, "Delivery not authorized, message refused" }, + { ESC_MAILING_LIST_EXPANSION_PROHIBITED, "Mailing list expansion prohibited" }, + { ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE,"Security conversion required but not possible" }, + { ESC_SECURITY_FEATURES_NOT_SUPPORTED, "Security features not supported" }, + { ESC_CRYPTOGRAPHIC_FAILURE, "Cryptographic failure" }, + { ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED, "Cryptographic algorithm not supported" }, + { ESC_MESSAGE_TOO_BIG_FOR_SYSTEM, "Message integrity failure" }, +}; + +#if 0 +struct esc_map { + uint8_t code; + const char *description; +}; + +static struct esc_map esc_class[] = { + { 2, "Success" }, + { 4, "Persistent Transient Failure"}, + { 5, "Permanent Failure" }, +}; + +static struct esc_map esc_subclass[] = { + { 0, "Other/Undefined" }, + { 1, "Addressing Status" }, + { 2, "Mailbox Status" }, + { 3, "Mail System Status" }, + { 4, "Network and Routing Status" }, + { 5, "Mail Delivery Protocol Status" }, + { 6, "Message Content or Media Status" }, + { 7, "Security or Policy Status" }, +}; +#endif + +const char * +esc_code(enum enhanced_status_class class, enum enhanced_status_code code) +{ + static char buffer[6]; + + snprintf(buffer, sizeof buffer, "%d.%d.%d", class, code / 10, code % 10); + return buffer; + +} + +const char * +esc_description(enum enhanced_status_code code) +{ + uint32_t i; + + for (i = 0; i < nitems(esc); ++i) + if (code == esc[i].code) + return esc[i].description; + return "Other/Undefined"; +} diff --git a/usr.sbin/smtpd/mda.c b/usr.sbin/smtpd/mda.c index ee5f8c96551..77afab1a5aa 100644 --- a/usr.sbin/smtpd/mda.c +++ b/usr.sbin/smtpd/mda.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mda.c,v 1.101 2014/02/04 14:56:03 eric Exp $ */ +/* $OpenBSD: mda.c,v 1.102 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -90,7 +90,7 @@ static void mda_sig_handler(int, short, void *); static int mda_check_loop(FILE *, struct mda_envelope *); static int mda_getlastline(int, char *, size_t); static void mda_done(struct mda_session *); -static void mda_fail(struct mda_user *, int, const char *); +static void mda_fail(struct mda_user *, int, const char *, enum enhanced_status_code); static void mda_drain(void); static void mda_log(const struct mda_envelope *, const char *, const char *); static struct mda_user *mda_user(const struct envelope *); @@ -137,10 +137,12 @@ mda_imsg(struct mproc *p, struct imsg *imsg) if (status == LKA_TEMPFAIL) mda_fail(u, 0, - "Temporary failure in user lookup"); + "Temporary failure in user lookup", + ESC_OTHER_ADDRESS_STATUS); else if (status == LKA_PERMFAIL) mda_fail(u, 1, - "Permanent failure in user lookup"); + "Permanent failure in user lookup", + ESC_DESTINATION_MAILBOX_HAS_MOVED); else { memmove(&u->userinfo, data, sz); u->flags &= ~USER_WAITINFO; @@ -204,7 +206,8 @@ mda_imsg(struct mproc *p, struct imsg *imsg) if (imsg->fd == -1) { log_debug("debug: mda: cannot get message fd"); - queue_tempfail(e->id, "Cannot get message fd"); + queue_tempfail(e->id, "Cannot get message fd", + ESC_OTHER_MAIL_SYSTEM_STATUS); mda_log(e, "TempFail", "Cannot get message fd"); mda_done(s); return; @@ -217,7 +220,8 @@ mda_imsg(struct mproc *p, struct imsg *imsg) if ((s->datafp = fdopen(imsg->fd, "r")) == NULL) { log_warn("warn: mda: fdopen"); close(imsg->fd); - queue_tempfail(e->id, "fdopen failed"); + queue_tempfail(e->id, "fdopen failed", + ESC_OTHER_MAIL_SYSTEM_STATUS); mda_log(e, "TempFail", "fdopen failed"); mda_done(s); return; @@ -245,7 +249,8 @@ mda_imsg(struct mproc *p, struct imsg *imsg) if (n == -1) { log_warn("warn: mda: " "fail to write delivery info"); - queue_tempfail(e->id, "Out of memory"); + queue_tempfail(e->id, "Out of memory", + ESC_OTHER_MAIL_SYSTEM_STATUS); mda_log(e, "TempFail", "Out of memory"); mda_done(s); return; @@ -336,7 +341,8 @@ mda_imsg(struct mproc *p, struct imsg *imsg) e = s->evp; if (imsg->fd == -1) { log_warn("warn: mda: fail to retrieve mda fd"); - queue_tempfail(e->id, "Cannot get mda fd"); + queue_tempfail(e->id, "Cannot get mda fd", + ESC_OTHER_MAIL_SYSTEM_STATUS); mda_log(e, "TempFail", "Cannot get mda fd"); mda_done(s); return; @@ -379,7 +385,8 @@ mda_imsg(struct mproc *p, struct imsg *imsg) /* update queue entry */ if (error) { - queue_tempfail(e->id, error); + queue_tempfail(e->id, error, + ESC_OTHER_MAIL_SYSTEM_STATUS); snprintf(buf, sizeof buf, "Error (%s)", error); mda_log(e, "TempFail", buf); } @@ -662,7 +669,7 @@ mda_getlastline(int fd, char *dst, size_t dstsz) } static void -mda_fail(struct mda_user *user, int permfail, const char *error) +mda_fail(struct mda_user *user, int permfail, const char *error, enum enhanced_status_code code) { struct mda_envelope *e; @@ -670,11 +677,11 @@ mda_fail(struct mda_user *user, int permfail, const char *error) TAILQ_REMOVE(&user->envelopes, e, entry); if (permfail) { mda_log(e, "PermFail", error); - queue_permfail(e->id, error); + queue_permfail(e->id, error, code); } else { mda_log(e, "TempFail", error); - queue_tempfail(e->id, error); + queue_tempfail(e->id, error, code); } mda_envelope_free(e); } diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c index 7d87d7f1ca8..6740425da7b 100644 --- a/usr.sbin/smtpd/mta.c +++ b/usr.sbin/smtpd/mta.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta.c,v 1.181 2014/02/04 15:22:39 eric Exp $ */ +/* $OpenBSD: mta.c,v 1.182 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -266,6 +266,16 @@ mta_imsg(struct mproc *p, struct imsg *imsg) if (strcmp(buf, e->dest)) e->rcpt = xstrdup(buf, "mta_envelope:rcpt"); e->task = task; + if (evp.dsn_orcpt.user[0] && evp.dsn_orcpt.domain[0]) { + snprintf(buf, sizeof buf, "%s@%s", + evp.dsn_orcpt.user, evp.dsn_orcpt.domain); + e->dsn_orcpt = xstrdup(buf, + "mta_envelope:dsn_orcpt"); + } + strlcpy(e->dsn_envid, evp.dsn_envid, + sizeof e->dsn_envid); + e->dsn_notify = evp.dsn_notify; + e->dsn_ret = evp.dsn_ret; TAILQ_INSERT_TAIL(&task->envelopes, e, entry); log_debug("debug: mta: received evp:%016" PRIx64 @@ -772,12 +782,15 @@ mta_delivery_flush_event(int fd, short event, void *arg) if (tree_poproot(&flush_evp, NULL, (void**)(&e))) { - if (e->delivery == IMSG_DELIVERY_OK) - queue_ok(e->id); - else if (e->delivery == IMSG_DELIVERY_TEMPFAIL) - queue_tempfail(e->id, e->status); + if (e->delivery == IMSG_DELIVERY_OK) { + m_create(p_queue, IMSG_DELIVERY_OK, 0, 0, -1); + m_add_evpid(p_queue, e->id); + m_add_int(p_queue, e->ext); + m_close(p_queue); + } else if (e->delivery == IMSG_DELIVERY_TEMPFAIL) + queue_tempfail(e->id, e->status, ESC_OTHER_STATUS); else if (e->delivery == IMSG_DELIVERY_PERMFAIL) - queue_permfail(e->id, e->status); + queue_permfail(e->id, e->status, ESC_OTHER_STATUS); else if (e->delivery == IMSG_DELIVERY_LOOP) queue_loop(e->id); else { @@ -790,6 +803,7 @@ mta_delivery_flush_event(int fd, short event, void *arg) free(e->dest); free(e->rcpt); + free(e->dsn_orcpt); free(e); tv.tv_sec = 0; diff --git a/usr.sbin/smtpd/mta_session.c b/usr.sbin/smtpd/mta_session.c index 3ac18ae822b..3443f49d166 100644 --- a/usr.sbin/smtpd/mta_session.c +++ b/usr.sbin/smtpd/mta_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta_session.c,v 1.53 2014/02/04 14:56:03 eric Exp $ */ +/* $OpenBSD: mta_session.c,v 1.54 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -146,6 +146,8 @@ static int mta_check_loop(FILE *); static void mta_start_tls(struct mta_session *); static int mta_verify_certificate(struct mta_session *); static struct mta_session *mta_tree_pop(struct tree *, uint64_t); +static const char * dsn_strret(enum dsn_ret); +static const char * dsn_strnotify(uint8_t); void mta_hoststat_update(const char *, const char *); void mta_hoststat_reschedule(const char *); @@ -576,6 +578,8 @@ mta_connect(struct mta_session *s) static void mta_enter_state(struct mta_session *s, int newstate) { + struct mta_envelope *e; + size_t envid_sz; int oldstate; ssize_t q; char ibuf[SMTPD_MAXLINESIZE]; @@ -760,15 +764,39 @@ mta_enter_state(struct mta_session *s, int newstate) break; case MTA_MAIL: + if (s->currevp == NULL) + s->currevp = TAILQ_FIRST(&s->task->envelopes); + + e = s->currevp; s->hangon = 0; s->msgtried++; - mta_send(s, "MAIL FROM:<%s>", s->task->sender); + envid_sz = strlen(e->dsn_envid); + if (s->ext & MTA_EXT_DSN) { + mta_send(s, "MAIL FROM:<%s> %s%s %s%s", + s->task->sender, + e->dsn_ret ? "RET=" : "", + e->dsn_ret ? dsn_strret(e->dsn_ret) : "", + envid_sz ? "ENVID=" : "", + envid_sz ? e->dsn_envid : ""); + } else + mta_send(s, "MAIL FROM:<%s>", s->task->sender); break; case MTA_RCPT: if (s->currevp == NULL) s->currevp = TAILQ_FIRST(&s->task->envelopes); - mta_send(s, "RCPT TO:<%s>", s->currevp->dest); + + e = s->currevp; + if (s->ext & MTA_EXT_DSN) { + mta_send(s, "RCPT TO:<%s> %s%s %s%s", + e->dest, + e->dsn_notify ? "NOTIFY=" : "", + e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "", + e->dsn_orcpt ? "ORCPT=" : "", + e->dsn_orcpt ? e->dsn_orcpt : ""); + } else + mta_send(s, "RCPT TO:<%s>", e->dest); + s->rcptcount++; break; @@ -1179,6 +1207,8 @@ mta_io(struct io *io, int evt) } else if (strcmp(msg, "PIPELINING") == 0) s->ext |= MTA_EXT_PIPELINING; + else if (strcmp(msg, "DSN") == 0) + s->ext |= MTA_EXT_DSN; } if (cont) @@ -1334,6 +1364,7 @@ mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t co /* we're about to log, associate session to envelope */ e->session = s->id; + e->ext = s->ext; /* XXX */ /* @@ -1549,6 +1580,45 @@ mta_verify_certificate(struct mta_session *s) return 1; } +static const char * +dsn_strret(enum dsn_ret ret) +{ + if (ret == DSN_RETHDRS) + return "HDRS"; + else if (ret == DSN_RETFULL) + return "FULL"; + else { + log_debug("mta: invalid ret %d", ret); + return "???"; + } +} + +static const char * +dsn_strnotify(uint8_t arg) +{ + static char buf[32]; + size_t sz; + + if (arg & DSN_SUCCESS) + strlcat(buf, "SUCCESS,", sizeof(buf)); + + if (arg & DSN_FAILURE) + strlcat(buf, "FAILURE,", sizeof(buf)); + + if (arg & DSN_DELAY) + strlcat(buf, "DELAY,", sizeof(buf)); + + if (arg & DSN_NEVER) + strlcat(buf, "NEVER,", sizeof(buf)); + + /* trim trailing comma */ + sz = strlen(buf); + if (sz) + buf[sz - 1] = '\0'; + + return (buf); +} + #define CASE(x) case x : return #x static const char * diff --git a/usr.sbin/smtpd/queue.c b/usr.sbin/smtpd/queue.c index 48e2c0f0af0..51173e9302d 100644 --- a/usr.sbin/smtpd/queue.c +++ b/usr.sbin/smtpd/queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue.c,v 1.158 2014/02/04 14:56:03 eric Exp $ */ +/* $OpenBSD: queue.c,v 1.159 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -68,8 +68,9 @@ queue_imsg(struct mproc *p, struct imsg *imsg) uint64_t reqid, evpid, holdq; uint32_t msgid; time_t nexttry; - int fd, ret, v, flags; + int fd, mta_ext, ret, v, flags, code; + memset(&bounce, 0, sizeof(struct delivery_bounce)); if (p->proc == PROC_SMTP) { switch (imsg->hdr.type) { @@ -213,9 +214,9 @@ queue_imsg(struct mproc *p, struct imsg *imsg) return; bounce.type = B_ERROR; - bounce.delay = 0; - bounce.expire = 0; envelope_set_errormsg(&evp, "Envelope expired"); + envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); + envelope_set_esc_code(&evp, ESC_DELIVERY_TIME_EXPIRED); queue_bounce(&evp, &bounce); queue_log(&evp, "Expire", "Envelope expired"); queue_envelope_delete(evpid); @@ -335,7 +336,25 @@ queue_imsg(struct mproc *p, struct imsg *imsg) case IMSG_DELIVERY_OK: m_msg(&m, imsg); m_get_evpid(&m, &evpid); + if (p->proc == PROC_MTA) + m_get_int(&m, &mta_ext); m_end(&m); + if (queue_envelope_load(evpid, &evp) == 0) { + log_warn("queue: dsn: failed to load envelope"); + return; + } + if (evp.dsn_notify & DSN_SUCCESS) { + bounce.type = B_DSN; + bounce.dsn_ret = evp.dsn_ret; + + if (p->proc == PROC_MDA) + queue_bounce(&evp, &bounce); + else if (p->proc == PROC_MTA && + (mta_ext & MTA_EXT_DSN) == 0) { + bounce.mta_without_dsn = 1; + queue_bounce(&evp, &bounce); + } + } queue_envelope_delete(evpid); m_create(p_scheduler, IMSG_DELIVERY_OK, 0, 0, -1); m_add_evpid(p_scheduler, evpid); @@ -346,6 +365,7 @@ queue_imsg(struct mproc *p, struct imsg *imsg) m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_string(&m, &reason); + m_get_int(&m, &code); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: tempfail: failed to load envelope"); @@ -356,6 +376,8 @@ queue_imsg(struct mproc *p, struct imsg *imsg) return; } envelope_set_errormsg(&evp, "%s", reason); + envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); + envelope_set_esc_code(&evp, code); evp.retry++; if (!queue_envelope_update(&evp)) log_warnx("warn: could not update envelope %016"PRIx64, evpid); @@ -368,6 +390,7 @@ queue_imsg(struct mproc *p, struct imsg *imsg) m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_string(&m, &reason); + m_get_int(&m, &code); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: permfail: failed to load envelope"); @@ -378,9 +401,9 @@ queue_imsg(struct mproc *p, struct imsg *imsg) return; } bounce.type = B_ERROR; - bounce.delay = 0; - bounce.expire = 0; envelope_set_errormsg(&evp, "%s", reason); + envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL); + envelope_set_esc_code(&evp, code); queue_bounce(&evp, &bounce); queue_envelope_delete(evpid); m_create(p_scheduler, IMSG_DELIVERY_PERMFAIL, 0, 0, -1); @@ -401,9 +424,9 @@ queue_imsg(struct mproc *p, struct imsg *imsg) return; } envelope_set_errormsg(&evp, "%s", "Loop detected"); + envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); + envelope_set_esc_code(&evp, ESC_ROUTING_LOOP_DETECTED); bounce.type = B_ERROR; - bounce.delay = 0; - bounce.expire = 0; queue_bounce(&evp, &bounce); queue_envelope_delete(evp.id); m_create(p_scheduler, IMSG_DELIVERY_LOOP, 0, 0, -1); @@ -480,6 +503,9 @@ queue_bounce(struct envelope *e, struct delivery_bounce *d) b.creation = time(NULL); b.expire = 3600 * 24 * 7; + if (e->dsn_notify & DSN_NEVER) + return; + if (b.id == 0) log_warnx("warn: queue_bounce: evpid=0"); if (evpid_to_msgid(b.id) == 0) @@ -657,20 +683,22 @@ queue_ok(uint64_t evpid) } void -queue_tempfail(uint64_t evpid, const char *reason) +queue_tempfail(uint64_t evpid, const char *reason, enum enhanced_status_code code) { m_create(p_queue, IMSG_DELIVERY_TEMPFAIL, 0, 0, -1); m_add_evpid(p_queue, evpid); m_add_string(p_queue, reason); + m_add_int(p_queue, (int)code); m_close(p_queue); } void -queue_permfail(uint64_t evpid, const char *reason) +queue_permfail(uint64_t evpid, const char *reason, enum enhanced_status_code code) { m_create(p_queue, IMSG_DELIVERY_PERMFAIL, 0, 0, -1); m_add_evpid(p_queue, evpid); m_add_string(p_queue, reason); + m_add_int(p_queue, (int)code); m_close(p_queue); } diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index 5b6ac71f3fc..513d4a3544a 100644 --- a/usr.sbin/smtpd/smtp_session.c +++ b/usr.sbin/smtpd/smtp_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp_session.c,v 1.194 2014/02/04 13:44:41 eric Exp $ */ +/* $OpenBSD: smtp_session.c,v 1.195 2014/02/04 15:44:05 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -98,6 +98,7 @@ enum smtp_command { CMD_RSET, CMD_QUIT, CMD_HELP, + CMD_WIZ, CMD_NOOP, }; @@ -166,6 +167,7 @@ static void smtp_enter_state(struct smtp_session *, int); static void smtp_reply(struct smtp_session *, char *, ...); static void smtp_command(struct smtp_session *, char *); static int smtp_parse_mail_args(struct smtp_session *, char *); +static int smtp_parse_rcpt_args(struct smtp_session *, char *); static void smtp_rfc4954_auth_plain(struct smtp_session *, char *); static void smtp_rfc4954_auth_login(struct smtp_session *, char *); static void smtp_message_write(struct smtp_session *, const char *); @@ -175,6 +177,7 @@ static void smtp_wait_mfa(struct smtp_session *s, int); static void smtp_free(struct smtp_session *, const char *); static const char *smtp_strstate(int); static int smtp_verify_certificate(struct smtp_session *); +static uint8_t dsn_notify_str_to_uint8(const char *); static void smtp_auth_failure_pause(struct smtp_session *); static void smtp_auth_failure_resume(int, short, void *); static int smtp_sni_callback(SSL *, int *, void *); @@ -190,6 +193,7 @@ static struct { int code; const char *cmd; } commands[] = { { CMD_RSET, "RSET" }, { CMD_QUIT, "QUIT" }, { CMD_HELP, "HELP" }, + { CMD_WIZ, "WIZ" }, { CMD_NOOP, "NOOP" }, { -1, NULL }, }; @@ -369,9 +373,11 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) s->evp.id = msgid_to_evpid(msgid); s->rcptcount = 0; s->phase = PHASE_TRANSACTION; - smtp_reply(s, "250 Ok"); + smtp_reply(s, "250 %s: Ok", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); } else { - smtp_reply(s, "421 Temporary Error"); + smtp_reply(s, "421 %s: Temporary Error", + esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); smtp_enter_state(s, STATE_QUIT); } m_end(&m); @@ -389,7 +395,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) (s->ofile = fdopen(imsg->fd, "w")) == NULL) { if (imsg->fd != -1) close(imsg->fd); - smtp_reply(s, "421 Temporary Error"); + smtp_reply(s, "421 %s: Temporary Error", + esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); smtp_enter_state(s, STATE_QUIT); io_reload(&s->io); return; @@ -466,7 +473,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) * RCPT only so we must cancel the whole transaction * and close the connection. */ - smtp_reply(s, "421 Temporary failure"); + smtp_reply(s, "421 %s: Temporary failure", + esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); smtp_enter_state(s, STATE_QUIT); } else { @@ -478,7 +486,9 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) s->destcount = 0; s->rcptcount++; s->kickcount--; - smtp_reply(s, "250 Recipient ok"); + smtp_reply(s, "250 %s %s: Recipient ok", + esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID), + esc_description(ESC_DESTINATION_ADDRESS_VALID)); } io_reload(&s->io); return; @@ -493,7 +503,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) m_create(p_mfa, IMSG_MFA_EVENT_ROLLBACK, 0, 0, -1); m_add_id(p_mfa, s->id); m_close(p_mfa); - smtp_reply(s, "421 Temporary failure"); + smtp_reply(s, "421 %s: Temporary failure", + esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); smtp_enter_state(s, STATE_QUIT); io_reload(&s->io); return; @@ -503,7 +514,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) m_add_id(p_mfa, s->id); m_close(p_mfa); - smtp_reply(s, "250 %08x Message accepted for delivery", + smtp_reply(s, "250 %s: %08x Message accepted for delivery", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS), evpid_to_msgid(s->evp.id)); TAILQ_FOREACH(rcpt, &s->rcpts, entry) { @@ -544,7 +556,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) "on session %016"PRIx64, user, s->id); s->kickcount = 0; s->flags |= SF_AUTHENTICATED; - smtp_reply(s, "235 Authentication succeeded"); + smtp_reply(s, "235 %s: Authentication succeeded", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); } else if (success == LKA_PERMFAIL) { log_info("smtp-in: Authentication failed for user %s " @@ -555,7 +568,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) else if (success == LKA_TEMPFAIL) { log_info("smtp-in: Authentication temporarily failed " "for user %s on session %016"PRIx64, user, s->id); - smtp_reply(s, "421 Temporary failure"); + smtp_reply(s, "421 %s: Temporary failure", + esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); } else fatalx("bad lka response"); @@ -683,6 +697,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code, smtp_reply(s, "250-8BITMIME"); smtp_reply(s, "250-ENHANCEDSTATUSCODES"); smtp_reply(s, "250-SIZE %zu", env->sc_maxsize); + smtp_reply(s, "250-DSN"); if (ADVERTISE_TLS(s)) smtp_reply(s, "250-STARTTLS"); if (ADVERTISE_AUTH(s)) @@ -826,7 +841,8 @@ smtp_io(struct io *io, int evt) if ((line == NULL && iobuf_len(&s->iobuf) >= SMTPD_MAXLINESIZE) || (line && len >= SMTPD_MAXLINESIZE)) { s->flags |= SF_BADINPUT; - smtp_reply(s, "500 Line too long"); + smtp_reply(s, "500 %s: Line too long", + esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); smtp_enter_state(s, STATE_QUIT); io_set_write(io); return; @@ -858,7 +874,9 @@ smtp_io(struct io *io, int evt) /* Pipelining not supported */ if (iobuf_len(&s->iobuf)) { s->flags |= SF_BADINPUT; - smtp_reply(s, "500 Pipelining not supported"); + smtp_reply(s, "500 %s %s: Pipelining not supported", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); smtp_enter_state(s, STATE_QUIT); io_set_write(io); return; @@ -933,8 +951,8 @@ smtp_io(struct io *io, int evt) static void smtp_command(struct smtp_session *s, char *line) { - char *args, *eom, *method; - int cmd, i; + char *args, *eom, *method; + int cmd, i; log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line); @@ -988,18 +1006,25 @@ smtp_command(struct smtp_session *s, char *line) case CMD_HELO: case CMD_EHLO: if (s->phase != PHASE_INIT) { - smtp_reply(s, "503 Already indentified"); + smtp_reply(s, "503 %s %s: Already indentified", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (args == NULL) { - smtp_reply(s, "501 %s requires domain name", + smtp_reply(s, "501 %s %s: %s requires domain name", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND), (cmd == CMD_HELO) ? "HELO" : "EHLO"); + break; } if (!valid_domainpart(args)) { - smtp_reply(s, "501 Invalid domain name"); + smtp_reply(s, "501 %s %s: Invalid domain name", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), + esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); break; } strlcpy(s->helo, args, sizeof(s->helo)); @@ -1023,45 +1048,62 @@ smtp_command(struct smtp_session *s, char *line) */ case CMD_STARTTLS: if (s->phase != PHASE_SETUP) { - smtp_reply(s, "503 Command not allowed at this point."); + smtp_reply(s, "503 %s %s: Command not allowed at this point.", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (!(s->listener->flags & F_STARTTLS)) { - smtp_reply(s, "503 Command not supported"); + smtp_reply(s, "503 %s %s: Command not supported", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (s->flags & SF_SECURE) { - smtp_reply(s, "503 Channel already secured"); + smtp_reply(s, "503 %s %s: Channel already secured", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (args != NULL) { - smtp_reply(s, "501 No parameters allowed"); + smtp_reply(s, "501 %s %s: No parameters allowed", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), + esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); break; } - smtp_reply(s, "220 Ready to start TLS"); + smtp_reply(s, "220 %s: Ready to start TLS", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); smtp_enter_state(s, STATE_TLS); break; case CMD_AUTH: if (s->phase != PHASE_SETUP) { - smtp_reply(s, "503 Command not allowed at this point."); + smtp_reply(s, "503 %s %s: Command not allowed at this point.", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (s->flags & SF_AUTHENTICATED) { - smtp_reply(s, "503 Already authenticated"); + smtp_reply(s, "503 %s %s: Already authenticated", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (!ADVERTISE_AUTH(s)) { - smtp_reply(s, "503 Command not supported"); + smtp_reply(s, "503 %s %s: Command not supported", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (args == NULL) { - smtp_reply(s, "501 No parameters given"); + smtp_reply(s, "501 %s %s: No parameters given", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), + esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); break; } @@ -1076,40 +1118,53 @@ smtp_command(struct smtp_session *s, char *line) else if (strcasecmp(method, "LOGIN") == 0) smtp_rfc4954_auth_login(s, eom); else - smtp_reply(s, "504 AUTH method \"%s\" not supported", + smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported", + esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED), + esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED), method); break; case CMD_MAIL_FROM: if (s->phase != PHASE_SETUP) { - smtp_reply(s, "503 Command not allowed at this point."); + smtp_reply(s, "503 %s %s: Command not allowed at this point.", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); + break; } if (s->listener->flags & F_STARTTLS_REQUIRE && !(s->flags & SF_SECURE)) { smtp_reply(s, - "530 Must issue a STARTTLS command first"); + "530 %s %s: Must issue a STARTTLS command first", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (s->listener->flags & F_AUTH_REQUIRE && !(s->flags & SF_AUTHENTICATED)) { smtp_reply(s, - "530 Must issue an AUTH command first"); + "530 %s %s: Must issue an AUTH command first", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (s->mailcount >= SMTP_LIMIT_MAIL) { - smtp_reply(s, "452 Too many messages sent"); + /* we can pretend we had too many recipients */ + smtp_reply(s, "452 %s %s: Too many messages sent", + esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), + esc_description(ESC_TOO_MANY_RECIPIENTS)); break; } smtp_message_reset(s, 1); if (smtp_mailaddr(&s->evp.sender, args, 1, &args, - s->smtpname) == 0) { - smtp_reply(s, "553 Sender address syntax error"); + s->smtpname) == 0) { + smtp_reply(s, "553 %s: Sender address syntax error", + esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); break; } if (args && smtp_parse_mail_args(s, args) == -1) @@ -1126,26 +1181,28 @@ smtp_command(struct smtp_session *s, char *line) */ case CMD_RCPT_TO: if (s->phase != PHASE_TRANSACTION) { - smtp_reply(s, "503 Command not allowed at this point."); + smtp_reply(s, "503 %s %s: Command not allowed at this point.", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (s->rcptcount >= SMTP_LIMIT_RCPT) { - smtp_reply(s, "452 Too many recipients"); + smtp_reply(s, "451 %s %s: Too many recipients", + esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), + esc_description(ESC_TOO_MANY_RECIPIENTS)); break; } if (smtp_mailaddr(&s->evp.rcpt, args, 0, &args, s->smtpname) == 0) { smtp_reply(s, - "553 Recipient address syntax error"); + "501 %s: Recipient address syntax error", + esc_code(ESC_STATUS_PERMFAIL, ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX)); break; } - if (*args) { - smtp_reply(s, - "553 No option supported on RCPT TO"); + if (args && smtp_parse_rcpt_args(s, args) == -1) break; - } m_create(p_mfa, IMSG_MFA_REQ_RCPT, 0, 0, -1); m_add_id(p_mfa, s->id); @@ -1156,7 +1213,9 @@ smtp_command(struct smtp_session *s, char *line) case CMD_RSET: if (s->phase != PHASE_TRANSACTION && s->phase != PHASE_SETUP) { - smtp_reply(s, "503 Command not allowed at this point."); + smtp_reply(s, "503 %s %s: Command not allowed at this point.", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } @@ -1172,16 +1231,21 @@ smtp_command(struct smtp_session *s, char *line) s->phase = PHASE_SETUP; smtp_message_reset(s, 0); - smtp_reply(s, "250 2.0.0 Reset state"); + smtp_reply(s, "250 %s: Reset state", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); break; case CMD_DATA: if (s->phase != PHASE_TRANSACTION) { - smtp_reply(s, "503 Command not allowed at this point."); + smtp_reply(s, "503 %s %s: Command not allowed at this point.", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } if (s->rcptcount == 0) { - smtp_reply(s, "503 5.5.1 No recipient specified"); + smtp_reply(s, "503 %s %s: No recipient specified", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), + esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); break; } @@ -1194,12 +1258,14 @@ smtp_command(struct smtp_session *s, char *line) * ANY */ case CMD_QUIT: - smtp_reply(s, "221 Bye"); + smtp_reply(s, "221 %s: Bye", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); smtp_enter_state(s, STATE_QUIT); break; case CMD_NOOP: - smtp_reply(s, "250 Ok"); + smtp_reply(s, "250 %s: Ok", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); break; case CMD_HELP: @@ -1207,11 +1273,20 @@ smtp_command(struct smtp_session *s, char *line) smtp_reply(s, "214- To report bugs in the implementation, " "please contact bugs@openbsd.org"); smtp_reply(s, "214- with full details"); - smtp_reply(s, "214 End of HELP info"); + smtp_reply(s, "214 %s: End of HELP info", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); + break; + + case CMD_WIZ: + smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; default: - smtp_reply(s, "500 Command unrecognized"); + smtp_reply(s, "500 %s %s: Command unrecognized", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), + esc_description(ESC_INVALID_COMMAND)); break; } } @@ -1270,7 +1345,9 @@ smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg) } abort: - smtp_reply(s, "501 Syntax error"); + smtp_reply(s, "501 %s %s: Syntax error", + esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), + esc_description(ESC_SYNTAX_ERROR)); smtp_enter_state(s, STATE_HELO); } @@ -1315,10 +1392,71 @@ smtp_rfc4954_auth_login(struct smtp_session *s, char *arg) } abort: - smtp_reply(s, "501 Syntax error"); + smtp_reply(s, "501 %s %s: Syntax error", + esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), + esc_description(ESC_SYNTAX_ERROR)); smtp_enter_state(s, STATE_HELO); } +static uint8_t +dsn_notify_str_to_uint8(const char *arg) +{ + if (strcasecmp(arg, "SUCCESS") == 0) + return DSN_SUCCESS; + else if (strcasecmp(arg, "FAILURE") == 0) + return DSN_FAILURE; + else if (strcasecmp(arg, "DELAY") == 0) + return DSN_DELAY; + else if (strcasecmp(arg, "NEVER") == 0) + return DSN_NEVER; + + return (0); +} + +static int +smtp_parse_rcpt_args(struct smtp_session *s, char *args) +{ + char *b, *p; + uint8_t flag; + + while ((b = strsep(&args, " "))) { + if (*b == '\0') + continue; + + if (strncasecmp(b, "NOTIFY=", 7) == 0) { + b += 7; + while ((p = strsep(&b, ","))) { + if (*p == '\0') + continue; + + if ((flag = dsn_notify_str_to_uint8(p)) == 0) + continue; + + s->evp.dsn_notify |= flag; + } + if (s->evp.dsn_notify & DSN_NEVER && + s->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE | + DSN_DELAY)) { + smtp_reply(s, + "553 NOTIFY option NEVER cannot be \ + combined with other options"); + return (-1); + } + } else if (strncasecmp(b, "ORCPT=", 6) == 0) { + b += 6; + if (!text_to_mailaddr(&s->evp.dsn_orcpt, b)) { + smtp_reply(s, "553 ORCPT address syntax error"); + return (-1); + } + } else { + smtp_reply(s, "503 Unsupported option %s", b); + return (-1); + } + } + + return (0); +} + static int smtp_parse_mail_args(struct smtp_session *s, char *args) { @@ -1332,13 +1470,24 @@ smtp_parse_mail_args(struct smtp_session *s, char *args) log_debug("debug: smtp: AUTH in MAIL FROM command"); else if (strncasecmp(b, "SIZE=", 5) == 0) log_debug("debug: smtp: SIZE in MAIL FROM command"); - else if (!strcasecmp(b, "BODY=7BIT")) + else if (strcasecmp(b, "BODY=7BIT") == 0) /* XXX only for this transaction */ s->flags &= ~SF_8BITMIME; else if (strcasecmp(b, "BODY=8BITMIME") == 0) ; - else { - smtp_reply(s, "503 Unsupported option %s", b); + else if (strncasecmp(b, "RET=", 4) == 0) { + b += 4; + if (strcasecmp(b, "HDRS") == 0) + s->evp.dsn_ret = DSN_RETHDRS; + else if (strcasecmp(b, "FULL") == 0) + s->evp.dsn_ret = DSN_RETFULL; + } else if (strncasecmp(b, "ENVID=", 6) == 0) { + b += 6; + strlcpy(s->evp.dsn_envid, b, sizeof(s->evp.dsn_envid)); + } else { + smtp_reply(s, "503 %s %s: Unsupported option %s", + esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), + esc_description(ESC_INVALID_COMMAND_ARGUMENTS), b); return (-1); } } @@ -1459,7 +1608,9 @@ smtp_message_end(struct smtp_session *s) m_add_msgid(p_queue, evpid_to_msgid(s->evp.id)); m_close(p_queue); if (s->msgflags & MF_ERROR_SIZE) - smtp_reply(s, "554 Message too big"); + smtp_reply(s, "554 %s %s: Transaction failed, message too big", + esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM), + esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM)); else smtp_reply(s, "%d Message rejected", s->msgcode); smtp_message_reset(s, 0); @@ -1765,6 +1916,7 @@ smtp_sni_callback(SSL *ssl, int *ad, void *arg) return SSL_TLSEXT_ERR_OK; } + #define CASE(x) case x : return #x const char * diff --git a/usr.sbin/smtpd/smtpd-api.h b/usr.sbin/smtpd/smtpd-api.h index 45984dd48bd..0570700cca8 100644 --- a/usr.sbin/smtpd/smtpd-api.h +++ b/usr.sbin/smtpd/smtpd-api.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd-api.h,v 1.15 2014/02/04 14:56:03 eric Exp $ */ +/* $OpenBSD: smtpd-api.h,v 1.16 2014/02/04 15:44:06 eric Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -214,6 +214,79 @@ enum { PROC_TABLE_FETCH, }; +enum enhanced_status_code { + /* 0.0 */ + ESC_OTHER_STATUS = 00, + + /* 1.x */ + ESC_OTHER_ADDRESS_STATUS = 10, + ESC_BAD_DESTINATION_MAILBOX_ADDRESS = 11, + ESC_BAD_DESTINATION_SYSTEM_ADDRESS = 12, + ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX = 13, + ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS = 14, + ESC_DESTINATION_ADDRESS_VALID = 15, + ESC_DESTINATION_MAILBOX_HAS_MOVED = 16, + ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX = 17, + ESC_BAD_SENDER_SYSTEM_ADDRESS = 18, + + /* 2.x */ + ESC_OTHER_MAILBOX_STATUS = 20, + ESC_MAILBOX_DISABLED = 21, + ESC_MAILBOX_FULL = 22, + ESC_MESSAGE_LENGTH_TOO_LARGE = 23, + ESC_MAILING_LIST_EXPANSION_PROBLEM = 24, + + /* 3.x */ + ESC_OTHER_MAIL_SYSTEM_STATUS = 30, + ESC_MAIL_SYSTEM_FULL = 31, + ESC_SYSTEM_NOT_ACCEPTING_MESSAGES = 32, + ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES = 33, + ESC_MESSAGE_TOO_BIG_FOR_SYSTEM = 34, + ESC_SYSTEM_INCORRECTLY_CONFIGURED = 35, + + /* 4.x */ + ESC_OTHER_NETWORK_ROUTING_STATUS = 40, + ESC_NO_ANSWER_FROM_HOST = 41, + ESC_BAD_CONNECTION = 42, + ESC_DIRECTORY_SERVER_FAILURE = 43, + ESC_UNABLE_TO_ROUTE = 44, + ESC_MAIL_SYSTEM_CONGESTION = 45, + ESC_ROUTING_LOOP_DETECTED = 46, + ESC_DELIVERY_TIME_EXPIRED = 47, + + /* 5.x */ + ESC_OTHER_PROTOCOL_STATUS = 50, + ESC_INVALID_COMMAND = 51, + ESC_SYNTAX_ERROR = 52, + ESC_TOO_MANY_RECIPIENTS = 53, + ESC_INVALID_COMMAND_ARGUMENTS = 54, + ESC_WRONG_PROTOCOL_VERSION = 55, + + /* 6.x */ + ESC_OTHER_MEDIA_ERROR = 60, + ESC_MEDIA_NOT_SUPPORTED = 61, + ESC_CONVERSION_REQUIRED_AND_PROHIBITED = 62, + ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED = 63, + ESC_CONVERSION_WITH_LOSS_PERFORMED = 64, + ESC_CONVERSION_FAILED = 65, + + /* 7.x */ + ESC_OTHER_SECURITY_STATUS = 70, + ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED = 71, + ESC_MAILING_LIST_EXPANSION_PROHIBITED = 72, + ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE = 73, + ESC_SECURITY_FEATURES_NOT_SUPPORTED = 74, + ESC_CRYPTOGRAPHIC_FAILURE = 75, + ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED = 76, + ESC_MESSAGE_INTEGRITY_FAILURE = 77, +}; + +enum enhanced_status_class { + ESC_STATUS_OK = 2, + ESC_STATUS_TEMPFAIL = 4, + ESC_STATUS_PERMFAIL = 5, +}; + static inline uint32_t evpid_to_msgid(uint64_t evpid) { @@ -243,6 +316,12 @@ int dict_iter(struct dict *, void **, const char **, void **); int dict_iterfrom(struct dict *, void **, const char *, const char **, void **); void dict_merge(struct dict *, struct dict *); + +/* esc.c */ +const char *esc_code(enum enhanced_status_class, enum enhanced_status_code); +const char *esc_description(enum enhanced_status_code); + + /* filter_api.c */ void filter_api_setugid(uid_t, gid_t); void filter_api_set_chroot(const char *); diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 64a62588fc5..70f39a3ff41 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.447 2014/02/04 15:22:39 eric Exp $ */ +/* $OpenBSD: smtpd.h,v 1.448 2014/02/04 15:44:06 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -91,6 +91,8 @@ #define RELAY_LMTP 0x80 #define RELAY_TLS_VERIFY 0x200 +#define MTA_EXT_DSN 0x400 + struct userinfo { char username[SMTPD_MAXLOGNAME]; char directory[SMTPD_MAXPATHLEN]; @@ -386,12 +388,20 @@ struct delivery_mta { enum bounce_type { B_ERROR, B_WARNING, + B_DSN +}; + +enum dsn_ret { + DSN_RETFULL = 1, + DSN_RETHDRS }; struct delivery_bounce { enum bounce_type type; time_t delay; time_t expire; + enum dsn_ret dsn_ret; + int mta_without_dsn; }; enum expand_type { @@ -435,6 +445,13 @@ struct expand { struct expandnode *parent; }; +#define DSN_SUCCESS 0x01 +#define DSN_FAILURE 0x02 +#define DSN_DELAY 0x04 +#define DSN_NEVER 0x08 + +#define DSN_ENVID_LEN 100 + #define SMTPD_ENVELOPE_VERSION 2 struct envelope { TAILQ_ENTRY(envelope) entry; @@ -468,6 +485,14 @@ struct envelope { time_t lasttry; time_t nexttry; time_t lastbounce; + + struct mailaddr dsn_orcpt; + char dsn_envid[DSN_ENVID_LEN+1]; + uint8_t dsn_notify; + enum dsn_ret dsn_ret; + + uint8_t esc_class; + uint8_t esc_code; }; struct listener { @@ -769,6 +794,13 @@ struct mta_envelope { char *rcpt; struct mta_task *task; int delivery; + + int ext; + char *dsn_orcpt; + char dsn_envid[DSN_ENVID_LEN+1]; + uint8_t dsn_notify; + enum dsn_ret dsn_ret; + char status[SMTPD_MAXLINESIZE]; }; @@ -1082,6 +1114,8 @@ int enqueue(int, char **); /* envelope.c */ void envelope_set_errormsg(struct envelope *, char *, ...); +void envelope_set_esc_class(struct envelope *, enum enhanced_status_class); +void envelope_set_esc_code(struct envelope *, enum enhanced_status_code); int envelope_load_buffer(struct envelope *, const char *, size_t); int envelope_dump_buffer(const struct envelope *, char *, size_t); @@ -1214,8 +1248,8 @@ int cmdline_symset(char *); /* queue.c */ pid_t queue(void); void queue_ok(uint64_t); -void queue_tempfail(uint64_t, const char *); -void queue_permfail(uint64_t, const char *); +void queue_tempfail(uint64_t, const char *, enum enhanced_status_code); +void queue_permfail(uint64_t, const char *, enum enhanced_status_code); void queue_loop(uint64_t); void queue_flow_control(void); diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index 0336f176b67..34820ea4098 100644 --- a/usr.sbin/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.68 2014/01/18 05:54:51 martynas Exp $ +# $OpenBSD: Makefile,v 1.69 2014/02/04 15:44:06 eric Exp $ .PATH: ${.CURDIR}/.. PROG= smtpd SRCS= aliases.c bounce.c ca.c compress_backend.c config.c \ - control.c crypto.c delivery.c dict.c dns.c envelope.c \ + control.c crypto.c delivery.c dict.c dns.c envelope.c esc.c \ expand.c forward.c iobuf.c ioev.c limit.c lka.c lka_session.c \ log.c mda.c mfa.c mfa_session.c mproc.c \ mta.c mta_session.c parse.y queue.c queue_backend.c \ |