From 15e3d453f414fc1b815ec4895960cad312e78ef9 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Sun, 11 Mar 2012 21:47:04 -0400 Subject: [PATCH] Require From: header in all outgoing messages. Allow Sender: header (require if multiple addresses are in From:). Support new Envelope-From: header for overriding post's choice of SMTP envelope-from address. --- man/mh-mail.man | 26 ++++-- man/send.man | 27 ++++--- uip/post.c | 242 ++++++++++++++++++++++++++++++++++--------------------- 3 files changed, 182 insertions(+), 113 deletions(-) diff --git a/man/mh-mail.man b/man/mh-mail.man index 69e5fe4..87a0535 100644 --- a/man/mh-mail.man +++ b/man/mh-mail.man @@ -99,12 +99,11 @@ profile entry. It contains the address of the author or authors (may be more than one if a \*(lqSender:\*(rq field is present). For a standard reply (using -.BR repl , +.BR repl ), the reply address is constructed by checking the following headers (in this order): \*(lqMail-Reply\-To:\*(rq, \*(lqReply\-To:\*(rq, \*(lqFrom:\*(rq, \*(lqSender:\*(rq. -A -.BR From : +A \*(lqFrom:\*(rq header MUST exist when the message is sent to .BR post, otherwise the message will be rejected. @@ -145,10 +144,11 @@ constructed by checking the following headers (in this order): .PP .BR Sender : .RS 5 -Added by +Required by .B post -in the event that the message already has a -\*(lqFrom:\*(rq line. This line contains the address of the actual +in the event that the message has multiple addresses on the +\*(lqFrom:\*(rq line. It is otherwise optional. This line should +contain the address of the actual sender. .RE .PP @@ -162,14 +162,24 @@ Contains addresses of primary recipients. Contains addresses of secondary recipients. .RE .PP -Bcc: +.BR Bcc : .RS 5 Still more recipients. However, the \*(lqBcc:\*(rq line is not copied onto the message as delivered, so these recipients are not listed. .B nmh uses an encapsulation method for blind copies, see -.BR send . +.BR send (1). +.RE +.PP +.BR Dcc : +.RS 5 +Still more recipients. However, the \*(lqDcc:\*(rq line is not +copied onto the messages as delivered. Recipients on the \*(lqDcc:\*(rq +line receive the same messsage as recipients on the \*(lqTo:\*(rq and +\*(lqcc:\*(rq lines. See +.BR send (1) +for more details. .RE .PP .BR Fcc : diff --git a/man/send.man b/man/send.man index faf4d08..f4fea40 100644 --- a/man/send.man +++ b/man/send.man @@ -278,16 +278,7 @@ switch, then will use the MIME rules for encapsulation. .PP -Prior to sending the message, the fields \*(lqFrom:\ user@local\*(rq, -and \*(lqDate:\ now\*(rq will be appended to the headers in the message. -If the environment variable -.B $SIGNATURE -is set, then its value -is used as your personal name when constructing the \*(lqFrom:\*(rq -line of the message. If this environment variable is not set, then -.B send -will consult the profile entry \*(lqSignature\*(rq for -this information. +Prior to sending the message, the \*(lqDate:\ now\*(rq field will be appended to the headers in the message. If .B \-msgid is specified, then a \*(lqMessage\-ID:\*(rq field will also @@ -299,9 +290,19 @@ is re\-distributing a message (when invoked by .BR dist ), then \*(lqResent\-\*(rq will be prepended to each of these fields: \*(lqFrom:\*(rq, \*(lqDate:\*(rq, and \*(lqMessage\-ID:\*(rq. -If the message already contains a \*(lqFrom:\*(rq field, then a -\*(lqSender: user@local\*(rq field will be added as well. (An already -existing \*(lqSender:\*(rq field is an error!) +.PP +A \*(lqFrom:\*(rq field is required for all outgoing messages. Multiple +addresses are permitted in the \*(lqFrom:\*(rq field, but a \*(lqSender:\*(rq +field is required in this case. Otherwise a \*(lqSender:\*(rq field +is optional. +.PP +When using SMTP for mail submission, the envelope\-from used for SMTP +transaction is derived from the \*(lqFrom:\*(rq field. If multiple +addresses are in the \*(lqFrom:\*(rq field, the address in the +\*(lqSender\*(rq field is used instead. This behavior can be overridden +by using the \*(lqEnvelope\-From:\*(rq field. When \*(lqEnvelope\-From\*(rq +appears in the message, its address will be used as the SMTP envelope\-from +address and the field will be removed from the message. .PP By using the .B \-format diff --git a/uip/post.c b/uip/post.c index 83e008d..79a53aa 100644 --- a/uip/post.c +++ b/uip/post.c @@ -158,6 +158,8 @@ struct headers { #define HNIL 0x0100 /* okay for this header not to have addrs */ #define HIGN 0x0200 /* ignore this header */ #define HDCC 0x0400 /* another undocumented feature */ +#define HONE 0x0800 /* Only one address allowed */ +#define HEFM 0x1000 /* Envelope-From: header */ /* * flags for headers->set @@ -167,29 +169,33 @@ struct headers { #define MRFM 0x0004 /* we've seen a Resent-From: */ #define MVIS 0x0008 /* we've seen sighted addrs */ #define MINV 0x0010 /* we've seen blind addrs */ +#define MSND 0x0020 /* we've seen a Sender: */ +#define MRSN 0x0040 /* We've seen a Resent-Sendr:*/ +#define MEFM 0x0080 /* We've seen Envelope-From: */ static struct headers NHeaders[] = { - { "Return-Path", HBAD, 0 }, - { "Received", HBAD, 0 }, - { "Reply-To", HADR|HNGR, 0 }, - { "From", HADR|HNGR, MFRM }, - { "Sender", HADR|HBAD, 0 }, - { "Date", HBAD, 0 }, - { "Subject", HSUB, 0 }, - { "To", HADR|HTRY, MVIS }, - { "cc", HADR|HTRY, MVIS }, - { "Bcc", HADR|HTRY|HBCC|HNIL, MINV }, - { "Dcc", HADR|HTRY|HDCC|HNIL, MVIS }, /* sorta cc & bcc combined */ - { "Message-ID", HBAD, 0 }, - { "Fcc", HFCC, 0 }, - { NULL, 0, 0 } + { "Return-Path", HBAD, 0 }, + { "Received", HBAD, 0 }, + { "Reply-To", HADR|HNGR, 0 }, + { "From", HADR|HNGR, MFRM }, + { "Sender", HADR|HNGR|HONE, MSND }, + { "Date", HBAD, 0 }, + { "Subject", HSUB, 0 }, + { "To", HADR|HTRY, MVIS }, + { "cc", HADR|HTRY, MVIS }, + { "Bcc", HADR|HTRY|HBCC|HNIL, MINV }, + { "Dcc", HADR|HTRY|HDCC|HNIL, MVIS }, /* sorta cc & bcc combined */ + { "Message-ID", HBAD, 0 }, + { "Fcc", HFCC, 0 }, + { "Envelope-From", HADR|HONE|HEFM, MEFM }, + { NULL, 0, 0 } }; static struct headers RHeaders[] = { { "Resent-Reply-To", HADR|HNGR, 0 }, { "Resent-From", HADR|HNGR, MRFM }, - { "Resent-Sender", HADR|HBAD, 0 }, + { "Resent-Sender", HADR|HNGR, MRSN }, { "Resent-Date", HBAD, 0 }, { "Resent-Subject", HSUB, 0 }, { "Resent-To", HADR|HTRY, MVIS }, @@ -199,12 +205,13 @@ static struct headers RHeaders[] = { { "Resent-Fcc", HFCC, 0 }, { "Reply-To", HADR, 0 }, { "From", HADR|HNGR, MFRM }, - { "Sender", HADR|HNGR, 0 }, + { "Sender", HADR|HNGR, MSND }, { "Date", HNOP, MDAT }, { "To", HADR|HNIL, 0 }, { "cc", HADR|HNIL, 0 }, { "Bcc", HADR|HTRY|HBCC|HNIL, 0 }, { "Fcc", HIGN, 0 }, + { "Envelope-From", HADR|HONE|HEFM, MEFM }, { NULL, 0, 0 } }; @@ -234,6 +241,8 @@ static char *saslmech=NULL; /* Force use of particular SASL mech */ static char *user=NULL; /* Authenticate as this user */ static char *port="smtp"; /* Name of server port for SMTP */ static int tls=0; /* Use TLS for encryption */ +static int fromcount=0; /* Count of addresses on From: header */ +static int seensender=0; /* Have we seen a Sender: header? */ static unsigned msgflags = 0; /* what we've seen */ @@ -249,6 +258,8 @@ static char tmpfil[BUFSIZ]; static char bccfil[BUFSIZ]; static char from[BUFSIZ]; /* my network address */ +static char sender[BUFSIZ]; /* my Sender: header */ +static char efrom[BUFSIZ]; /* my Envelope-From: header */ static char signature[BUFSIZ]; /* my signature */ static char *filter = NULL; /* the filter for BCC'ing */ static char *subject = NULL; /* the subject field for BCC'ing */ @@ -272,8 +283,6 @@ static char prefix[] = "----- =_aaaaaaaaaa"; static char *partno = NULL; static int queued = 0; -extern boolean draft_from_masquerading; /* defined in mts.c */ - /* * static prototypes */ @@ -289,14 +298,14 @@ static void anno (void); static int annoaux (struct mailname *); static void insert_fcc (struct headers *, unsigned char *); static void make_bcc_file (int); -static void verify_all_addresses (int); +static void verify_all_addresses (int, char *); static void chkadr (void); static void sigon (void); static void sigoff (void); static void p_refile (char *); static void fcc (char *, char *); static void die (char *, char *, ...); -static void post (char *, int, int); +static void post (char *, int, int, char *); static void do_text (char *file, int fd); static void do_an_address (struct mailname *, int); static void do_addresses (int, int); @@ -307,7 +316,7 @@ int main (int argc, char **argv) { int state, compnum, dashstuff = 0; - char *cp, *msg = NULL, **argp, **arguments; + char *cp, *msg = NULL, **argp, **arguments, *envelope; char buf[BUFSIZ], name[NAMESZ]; FILE *in, *out; @@ -608,22 +617,40 @@ main (int argc, char **argv) fclose (out); } + /* + * Here's how we decide which address to use as the envelope-from + * address for SMTP. + * + * - If we were given an Envelope-From header, use that. + * - If we were given multiple addresses in the From: header, use + * the Sender: address + * - Otherwise, use the address on the From: line + */ + + if (msgflags & MEFM) { + envelope = efrom; + } else if (fromcount > 1) { + envelope = sender; + } else { + envelope = from; + } + /* If we are doing a "whom" check */ if (whomsw) { - verify_all_addresses (1); + verify_all_addresses (1, envelope); done (0); } if (msgflags & MINV) { make_bcc_file (dashstuff); if (msgflags & MVIS) { - verify_all_addresses (verbose); - post (tmpfil, 0, verbose); + verify_all_addresses (verbose, envelope); + post (tmpfil, 0, verbose, envelope); } - post (bccfil, 1, verbose); + post (bccfil, 1, verbose, envelope); unlink (bccfil); } else { - post (tmpfil, 0, isatty (1)); + post (tmpfil, 0, isatty (1), envelope); } p_refile (tmpfil); @@ -720,6 +747,12 @@ putfmt (char *name, char *str, FILE *out) return; } + if (count > 1 && (hdr->flags & HONE)) { + advise (NULL, "%s: field only permits one address", name); + badmsg++; + return; + } + nameoutput = linepos = 0; snprintf (namep, sizeof(namep), "%s%s", (hdr->flags & HMNG) ? "Original-" : "", name); @@ -738,17 +771,37 @@ putfmt (char *name, char *str, FILE *out) continue; } - if (draft_from_masquerading && ((msgstate == RESENT) - ? (hdr->set & MRFM) - : (hdr->set & MFRM))) - /* The user manually specified a [Resent-]From: address in - their draft and the "masquerade:" line in mts.conf - doesn't contain "draft_from", so we'll set things up to - use the actual email address embedded in the draft - [Resent-]From: (after alias substitution, and without the - GECOS full name or angle brackets) as the envelope - From:. */ + /* + * If it's a From: or Resent-From: header, save the address + * for later possible use (as the envelope address for SMTP) + */ + + if ((msgstate == RESENT) ? (hdr->set & MRFM) + : (hdr->set & MFRM)) { strncpy(from, auxformat(mp, 0), sizeof(from) - 1); + from[sizeof(from) - 1] = '\0'; + fromcount = count; + } + + /* + * Also save the Sender: or Resent-Sender: header as well + */ + + if ((msgstate == RESENT) ? (hdr->set & MRSN) + : (hdr->set & MSND)) { + strncpy(sender, auxformat(mp, 0), sizeof(sender) - 1); + sender[sizeof(sender) - 1] = '\0'; + seensender++; + } + + /* + * ALSO ... save Envelope-From + */ + + if (hdr->set & MEFM) { + strncpy(efrom, auxformat(mp, 0), sizeof(efrom) - 1); + efrom[sizeof(efrom) - 1] = '\0'; + } if (hdr->flags & HBCC) mp->m_bcc++; @@ -770,17 +823,37 @@ putfmt (char *name, char *str, FILE *out) } else { /* Address includes a host, so no alias substitution is needed. */ - if (draft_from_masquerading && ((msgstate == RESENT) - ? (hdr->set & MRFM) - : (hdr->set & MFRM))) - /* The user manually specified a [Resent-]From: address in - their draft and the "masquerade:" line in mts.conf - doesn't contain "draft_from", so we'll set things up to - use the actual email address embedded in the draft - [Resent-]From: (after alias substitution, and without the - GECOS full name or angle brackets) as the envelope - From:. */ + + /* + * If it's a From: or Resent-From header, save the address + * for later possible use (as the envelope address for SMTP) + */ + + if ((msgstate == RESENT) ? (hdr->set & MRFM) + : (hdr->set & MFRM)) { strncpy(from, auxformat(mp, 0), sizeof(from) - 1); + fromcount = count; + } + + /* + * Also save the Sender: header as well + */ + + if ((msgstate == RESENT) ? (hdr->set & MRSN) + : (hdr->set & MSND)) { + strncpy(sender, auxformat(mp, 0), sizeof(sender) - 1); + sender[sizeof(sender) - 1] = '\0'; + seensender++; + } + + /* + * ALSO ... save Envelope-From + */ + + if (hdr->set & MEFM) { + strncpy(efrom, auxformat(mp, 0), sizeof(efrom) - 1); + efrom[sizeof(efrom) - 1] = '\0'; + } if (hdr->flags & HBCC) mp->m_bcc++; @@ -853,6 +926,21 @@ finish_headers (FILE *out) { switch (msgstate) { case NORMAL: + if (!(msgflags & MFRM)) { + /* + * A From: header is now required in the draft. + */ + advise (NULL, "message has no From: header"); + advise (NULL, "See default components files for examples"); + badmsg++; + } + + if (fromcount > 1 && seensender == 0) { + advise (NULL, "A Sender: header is required with multiple " + "From: addresses"); + badmsg++; + } + if (whomsw) break; @@ -860,21 +948,6 @@ finish_headers (FILE *out) if (msgid) fprintf (out, "Message-ID: <%d.%ld@%s>\n", (int) getpid (), (long) tclock, LocalName (1)); - if (msgflags & MFRM) { - /* There was already a From: in the draft. Don't add one. */ - if (!draft_from_masquerading) - /* mts.conf didn't contain "masquerade:[...]draft_from[...]" - so we'll reveal the user's actual account@thismachine - address in a Sender: header (and use it as the envelope - From: later). */ - fprintf (out, "Sender: %s\n", from); - } - else - /* Construct a From: header. */ - fprintf (out, "From: %s\n", signature); - if (whomsw) - break; - if (!(msgflags & MVIS)) fprintf (out, "Bcc: Blind Distribution List: ;\n"); break; @@ -888,6 +961,17 @@ finish_headers (FILE *out) advise (NULL, "message has no From: header"); badmsg++; } + if (!(msgflags & MRFM)) { + advise (NULL, "message has no Resent-From: header"); + advise (NULL, "See default components files for examples"); + badmsg++; + } + if (fromcount > 1 && seensender == 0) { + advise (NULL, "A Resent-Sender: header is required with " + "multiple Resent-From: addresses"); + badmsg++; + } + if (whomsw) break; @@ -895,20 +979,6 @@ finish_headers (FILE *out) if (msgid) fprintf (out, "Resent-Message-ID: <%d.%ld@%s>\n", (int) getpid (), (long) tclock, LocalName (1)); - if (msgflags & MRFM) { - /* There was already a Resent-From: in draft. Don't add one. */ - if (!draft_from_masquerading) - /* mts.conf didn't contain "masquerade:[...]draft_from[...]" - so we'll reveal the user's actual account@thismachine - address in a Sender: header (and use it as the envelope - From: later). */ - fprintf (out, "Resent-Sender: %s\n", from); - } - else - /* Construct a Resent-From: header. */ - fprintf (out, "Resent-From: %s\n", signature); - if (whomsw) - break; if (!(msgflags & MVIS)) fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n"); break; @@ -943,7 +1013,7 @@ putadr (char *name, char *aka, struct mailname *mp, FILE *out, unsigned int flag if (mp->m_mbox == NULL || ((flags & HTRY) && !insert (mp))) return 0; - if ((flags & (HBCC | HDCC)) || mp->m_ingrp) + if ((flags & (HBCC | HDCC | HEFM)) || mp->m_ingrp) return 1; if (!nameoutput) { @@ -1147,18 +1217,6 @@ make_bcc_file (int dashstuff) if (msgid) fprintf (out, "Message-ID: <%d.%ld@%s>\n", (int) getpid (), (long) tclock, LocalName (1)); - if (msgflags & MFRM) { - /* There was already a From: in the draft. Don't add one. */ - if (!draft_from_masquerading) - /* mts.conf didn't contain "masquerade:[...]draft_from[...]" - so we'll reveal the user's actual account@thismachine - address in a Sender: header (and use it as the envelope - From: later). */ - fprintf (out, "Sender: %s\n", from); - } - else - /* Construct a From: header. */ - fprintf (out, "From: %s\n", signature); if (subject) fprintf (out, "Subject: %s", subject); fprintf (out, "BCC:\n"); @@ -1359,7 +1417,7 @@ do_addresses (int bccque, int talk) */ static void -post (char *file, int bccque, int talk) +post (char *file, int bccque, int talk, char *envelope) { int fd, onex; int retval; @@ -1378,7 +1436,7 @@ post (char *file, int bccque, int talk) if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, verbose, snoop, onex, queued, sasl, saslssf, saslmech, user, tls)) - || rp_isbad (retval = sm_winit (from))) + || rp_isbad (retval = sm_winit (envelope))) die (NULL, "problem initializing server; %s", rp_string (retval)); do_addresses (bccque, talk && verbose); @@ -1406,7 +1464,7 @@ post (char *file, int bccque, int talk) /* Address Verification */ static void -verify_all_addresses (int talk) +verify_all_addresses (int talk, char *envelope) { int retval; struct mailname *lp; @@ -1417,7 +1475,7 @@ verify_all_addresses (int talk) if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, verbose, snoop, 0, queued, sasl, saslssf, saslmech, user, tls)) - || rp_isbad (retval = sm_winit (from))) + || rp_isbad (retval = sm_winit (envelope))) die (NULL, "problem initializing server; %s", rp_string (retval)); if (talk && !whomsw) -- 1.7.10.4