diff options
Diffstat (limited to 'usr.sbin/sendmail/src/srvrsmtp.c')
| -rw-r--r-- | usr.sbin/sendmail/src/srvrsmtp.c | 635 |
1 files changed, 444 insertions, 191 deletions
diff --git a/usr.sbin/sendmail/src/srvrsmtp.c b/usr.sbin/sendmail/src/srvrsmtp.c index e2a09e400f52..69a4fbe35007 100644 --- a/usr.sbin/sendmail/src/srvrsmtp.c +++ b/usr.sbin/sendmail/src/srvrsmtp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1983, 1995 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * @@ -36,9 +36,9 @@ #ifndef lint #ifdef SMTP -static char sccsid[] = "@(#)srvrsmtp.c 8.37 (Berkeley) 4/13/94 (with SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.97 (Berkeley) 11/18/95 (with SMTP)"; #else -static char sccsid[] = "@(#)srvrsmtp.c 8.37 (Berkeley) 4/13/94 (without SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.97 (Berkeley) 11/18/95 (without SMTP)"; #endif #endif /* not lint */ @@ -117,35 +117,34 @@ bool OneXact = FALSE; /* one xaction only this run */ char *CurSmtpClient; /* who's at the other end of channel */ static char *skipword(); -extern char RealUserName[]; #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ +void smtp(e) - register ENVELOPE *e; + register ENVELOPE *volatile e; { register char *p; register struct cmd *c; char *cmd; auto ADDRESS *vrfyqueue; ADDRESS *a; - bool gotmail; /* mail command received */ - bool gothello; /* helo command received */ + volatile bool gotmail; /* mail command received */ + volatile bool gothello; /* helo command received */ bool vrfy; /* set if this is a vrfy command */ - char *protocol; /* sending protocol */ - char *sendinghost; /* sending hostname */ - unsigned long msize; /* approximate maximum message size */ - char *peerhostname; /* name of SMTP peer or "localhost" */ + char *volatile protocol; /* sending protocol */ + char *volatile sendinghost; /* sending hostname */ + char *volatile peerhostname; /* name of SMTP peer or "localhost" */ auto char *delimptr; char *id; - int nrcpts; /* number of RCPT commands */ + volatile int nrcpts = 0; /* number of RCPT commands */ bool doublequeue; - int badcommands = 0; /* count of bad commands */ + volatile int badcommands = 0; /* count of bad commands */ char inp[MAXLINE]; char cmdbuf[MAXLINE]; - extern char Version[]; extern ENVELOPE BlankEnvelope; + extern void help __P((char *)); if (fileno(OutChannel) != fileno(stdout)) { @@ -162,28 +161,41 @@ smtp(e) CurSmtpClient = CurHostName; setproctitle("server %s startup", CurSmtpClient); - expand("\201e", inp, &inp[sizeof inp], e); - if (BrokenSmtpPeers) +#ifdef LOG + if (LogLevel > 11) { - p = strchr(inp, '\n'); - if (p != NULL) - *p = '\0'; - message("220 %s", inp); + /* log connection information */ + syslog(LOG_INFO, "SMTP connect from %.100s (%.100s)", + CurSmtpClient, anynet_ntoa(&RealHostAddr)); } - else - { - char *q = inp; +#endif - while (q != NULL) - { - p = strchr(q, '\n'); - if (p != NULL) - *p++ = '\0'; - message("220-%s", q); - q = p; - } - message("220 ESMTP spoken here"); + /* output the first line, inserting "ESMTP" as second word */ + expand(SmtpGreeting, inp, sizeof inp, e); + p = strchr(inp, '\n'); + if (p != NULL) + *p++ = '\0'; + id = strchr(inp, ' '); + if (id == NULL) + id = &inp[strlen(inp)]; + cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; + message(cmd, id - inp, inp, id); + + /* output remaining lines */ + while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) + { + *p++ = '\0'; + if (isascii(*id) && isspace(*id)) + id++; + message("220-%s", id); + } + if (id != NULL) + { + if (isascii(*id) && isspace(*id)) + id++; + message("220 %s", id); } + protocol = NULL; sendinghost = macvalue('s', e); gothello = FALSE; @@ -213,7 +225,7 @@ smtp(e) /* read the input line */ SmtpPhase = "server cmd read"; - setproctitle("server %s cmd read", CurHostName); + setproctitle("server %s cmd read", CurSmtpClient); p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, SmtpPhase); @@ -226,7 +238,7 @@ smtp(e) MyHostName, CurSmtpClient); #ifdef LOG if (LogLevel > (gotmail ? 1 : 19)) - syslog(LOG_NOTICE, "lost input channel from %s", + syslog(LOG_NOTICE, "lost input channel from %.100s", CurSmtpClient); #endif if (InChild) @@ -285,6 +297,39 @@ smtp(e) protocol = "SMTP"; SmtpPhase = "server HELO"; } + + /* check for valid domain name (re 1123 5.2.5) */ + if (*p == '\0') + { + message("501 %s requires domain address", + cmdbuf); + break; + } + else + { + register char *q; + + for (q = p; *q != '\0'; q++) + { + if (!isascii(*q)) + break; + if (isalnum(*q)) + continue; + if (isspace(*q)) + { + *q = '\0'; + break; + } + if (strchr("[].-_#", *q) == NULL) + break; + } + if (*q != '\0') + { + message("501 Invalid domain name"); + break; + } + } + sendinghost = newstr(p); gothello = TRUE; if (c->cmdcode != CMDEHLO) @@ -300,10 +345,19 @@ smtp(e) MyHostName, CurSmtpClient); if (!bitset(PRIV_NOEXPN, PrivacyFlags)) message("250-EXPN"); +#if MIME8TO7 + message("250-8BITMIME"); +#endif if (MaxMessageSize > 0) message("250-SIZE %ld", MaxMessageSize); else message("250-SIZE"); +#if DSN + if (SendMIMEErrors) + message("250-DSN"); +#endif + message("250-VERB"); + message("250-ONEX"); message("250 HELP"); break; @@ -344,7 +398,7 @@ smtp(e) { auth_warning(e, "Host %s didn't use HELO protocol", - peerhostname); + CurSmtpClient); } #ifdef PICKY_HELO_CHECK if (strcasecmp(sendinghost, peerhostname) != 0 && @@ -352,7 +406,7 @@ smtp(e) strcasecmp(sendinghost, MyHostName) != 0)) { auth_warning(e, "Host %s claimed to be %s", - peerhostname, sendinghost); + CurSmtpClient, sendinghost); } #endif @@ -362,7 +416,7 @@ smtp(e) define('s', sendinghost, e); initsys(e); nrcpts = 0; - e->e_flags |= EF_LOGSENDER; + e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); /* child -- go do the processing */ @@ -392,19 +446,21 @@ smtp(e) /* check for possible spoofing */ if (RealUid != 0 && OpMode == MD_SMTP && - (e->e_from.q_mailer != LocalMailer && - strcmp(e->e_from.q_user, RealUserName) != 0)) + !wordinclass(RealUserName, 't') && + !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && + strcmp(e->e_from.q_user, RealUserName) != 0) { auth_warning(e, "%s owned process doing -bs", RealUserName); } /* now parse ESMTP arguments */ - msize = 0; + e->e_msgsize = 0; while (p != NULL && *p != '\0') { char *kp; char *vp = NULL; + extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); /* locate the beginning of the keyword */ while (isascii(*p) && isspace(*p)) @@ -435,59 +491,17 @@ smtp(e) printf("MAIL: got arg %s=\"%s\"\n", kp, vp == NULL ? "<null>" : vp); - if (strcasecmp(kp, "size") == 0) - { - if (vp == NULL) - { - usrerr("501 SIZE requires a value"); - /* NOTREACHED */ - } -# ifdef __STDC__ - msize = strtoul(vp, (char **) NULL, 10); -# else - msize = strtol(vp, (char **) NULL, 10); -# endif - } - else if (strcasecmp(kp, "body") == 0) - { - if (vp == NULL) - { - usrerr("501 BODY requires a value"); - /* NOTREACHED */ - } -# ifdef MIME - if (strcasecmp(vp, "8bitmime") == 0) - { - e->e_bodytype = "8BITMIME"; - SevenBit = FALSE; - } - else if (strcasecmp(vp, "7bit") == 0) - { - e->e_bodytype = "7BIT"; - SevenBit = TRUE; - } - else - { - usrerr("501 Unknown BODY type %s", - vp); - } -# endif - } - else - { - usrerr("501 %s parameter unrecognized", kp); - /* NOTREACHED */ - } + mail_esmtp_args(kp, vp, e); } - if (MaxMessageSize > 0 && msize > MaxMessageSize) + if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) { usrerr("552 Message size exceeds fixed maximum message size (%ld)", MaxMessageSize); /* NOTREACHED */ } - - if (!enoughspace(msize)) + + if (!enoughdiskspace(e->e_msgsize)) { message("452 Insufficient disk space; try again later"); break; @@ -517,11 +531,53 @@ smtp(e) p = skipword(p, "to"); if (p == NULL) break; - a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); + a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); if (a == NULL) break; - a->q_flags |= QPRIMARY; - a = recipient(a, &e->e_sendqueue, e); + p = delimptr; + + /* now parse ESMTP arguments */ + while (p != NULL && *p != '\0') + { + char *kp; + char *vp = NULL; + extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); + + /* locate the beginning of the keyword */ + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + break; + kp = p; + + /* skip to the value portion */ + while (isascii(*p) && isalnum(*p) || *p == '-') + p++; + if (*p == '=') + { + *p++ = '\0'; + vp = p; + + /* skip to the end of the value */ + while (*p != '\0' && *p != ' ' && + !(isascii(*p) && iscntrl(*p)) && + *p != '=') + p++; + } + + if (*p != '\0') + *p++ = '\0'; + + if (tTd(19, 1)) + printf("RCPT: got arg %s=\"%s\"\n", kp, + vp == NULL ? "<null>" : vp); + + rcpt_esmtp_args(a, kp, vp, e); + } + + /* save in recipient list after ESMTP mods */ + a = recipient(a, &e->e_sendqueue, 0, e); + if (Errors != 0) break; @@ -575,10 +631,18 @@ smtp(e) /* collect the text of the message */ SmtpPhase = "collect"; - collect(TRUE, doublequeue, e); + buffer_errors(); + collect(InChannel, TRUE, doublequeue, NULL, e); + flush_errors(TRUE); if (Errors != 0) goto abortmessage; - HoldErrs = TRUE; + + /* make sure we actually do delivery */ + e->e_flags &= ~EF_CLRQUEUE; + + /* from now on, we have to operate silently */ + buffer_errors(); + e->e_errormode = EM_MAIL; /* ** Arrange to send to everyone. @@ -599,42 +663,32 @@ smtp(e) */ SmtpPhase = "delivery"; - if (nrcpts != 1 && !doublequeue) - { - HoldErrs = TRUE; - e->e_errormode = EM_MAIL; - } e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); id = e->e_id; - /* send to all recipients */ - sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); - e->e_to = NULL; - - /* issue success if appropriate and reset */ - if (Errors == 0 || HoldErrs) - message("250 %s Message accepted for delivery", id); - - if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) + if (doublequeue) { - /* avoid sending back an extra message */ - e->e_flags &= ~EF_FATALERRS; - e->e_flags |= EF_CLRQUEUE; + /* make sure it is in the queue */ + queueup(e, FALSE); } else { - /* from now on, we have to operate silently */ - HoldErrs = TRUE; - e->e_errormode = EM_MAIL; + /* send to all recipients */ + sendall(e, SM_DEFAULT); + } + e->e_to = NULL; - /* if we just queued, poke it */ - if (doublequeue && e->e_sendmode != SM_QUEUE) - { - extern pid_t dowork(); + /* issue success message */ + message("250 %s Message accepted for delivery", id); - unlockqueue(e); - (void) dowork(id, TRUE, TRUE, e); - } + /* if we just queued, poke it */ + if (doublequeue && e->e_sendmode != SM_QUEUE && + e->e_sendmode != SM_DEFER) + { + extern pid_t dowork(); + + unlockqueue(e); + (void) dowork(id, TRUE, TRUE, e); } abortmessage: @@ -651,6 +705,9 @@ smtp(e) case CMDRSET: /* rset -- reset state */ message("250 Reset state"); + + /* arrange to ignore any current send list */ + e->e_sendqueue = NULL; e->e_flags |= EF_CLRQUEUE; if (InChild) finis(); @@ -668,13 +725,14 @@ smtp(e) PrivacyFlags)) { if (vrfy) - message("252 Who's to say?"); + message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); else message("502 Sorry, we do not allow this operation"); #ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%s: %s [rejected]", - CurSmtpClient, inp); + syslog(LOG_INFO, "%.100s: %s [rejected]", + CurSmtpClient, + shortenstring(inp, 203)); #endif break; } @@ -689,14 +747,16 @@ smtp(e) break; #ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); + syslog(LOG_INFO, "%.100s: %s", + CurSmtpClient, + shortenstring(inp, 203)); #endif vrfyqueue = NULL; QuickAbort = TRUE; if (vrfy) e->e_flags |= EF_VRFYONLY; while (*p != '\0' && isascii(*p) && isspace(*p)) - *p++; + p++; if (*p == '\0') { message("501 Argument required"); @@ -704,7 +764,7 @@ smtp(e) } else { - (void) sendtolist(p, NULLADDR, &vrfyqueue, e); + (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); } if (Errors != 0) { @@ -718,6 +778,8 @@ smtp(e) } while (vrfyqueue != NULL) { + extern void printvrfyaddr __P((ADDRESS *, bool)); + a = vrfyqueue; while ((a = a->q_next) != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) @@ -742,6 +804,9 @@ smtp(e) message("221 %s closing connection", MyHostName); doquit: + /* arrange to ignore any current send list */ + e->e_sendqueue = NULL; + /* avoid future 050 messages */ disconnect(1, e); @@ -786,8 +851,8 @@ doquit: # ifdef LOG if (LogLevel > 0) syslog(LOG_CRIT, - "\"%s\" command from %s (%s)", - c->cmdname, peerhostname, + "\"%s\" command from %.100s (%.100s)", + c->cmdname, CurSmtpClient, anynet_ntoa(&RealHostAddr)); # endif /* FALL THROUGH */ @@ -847,7 +912,7 @@ skipword(p, w) { syntax: message("501 Syntax error in parameters scanning \"%s\"", - firstp); + shortenstring(firstp, 203)); Errors++; return (NULL); } @@ -865,6 +930,183 @@ skipword(p, w) return (p); } /* +** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line +** +** Parameters: +** kp -- the parameter key. +** vp -- the value of that parameter. +** e -- the envelope. +** +** Returns: +** none. +*/ + +void +mail_esmtp_args(kp, vp, e) + char *kp; + char *vp; + ENVELOPE *e; +{ + if (strcasecmp(kp, "size") == 0) + { + if (vp == NULL) + { + usrerr("501 SIZE requires a value"); + /* NOTREACHED */ + } +# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) + e->e_msgsize = strtoul(vp, (char **) NULL, 10); +# else + e->e_msgsize = strtol(vp, (char **) NULL, 10); +# endif + } + else if (strcasecmp(kp, "body") == 0) + { + if (vp == NULL) + { + usrerr("501 BODY requires a value"); + /* NOTREACHED */ + } + else if (strcasecmp(vp, "8bitmime") == 0) + { + SevenBitInput = FALSE; + } + else if (strcasecmp(vp, "7bit") == 0) + { + SevenBitInput = TRUE; + } + else + { + usrerr("501 Unknown BODY type %s", + vp); + /* NOTREACHED */ + } + e->e_bodytype = newstr(vp); + } + else if (strcasecmp(kp, "envid") == 0) + { + if (vp == NULL) + { + usrerr("501 ENVID requires a value"); + /* NOTREACHED */ + } + if (!xtextok(vp)) + { + usrerr("501 Syntax error in ENVID parameter value"); + /* NOTREACHED */ + } + if (e->e_envid != NULL) + { + usrerr("501 Duplicate ENVID parameter"); + /* NOTREACHED */ + } + e->e_envid = newstr(vp); + } + else if (strcasecmp(kp, "ret") == 0) + { + if (vp == NULL) + { + usrerr("501 RET requires a value"); + /* NOTREACHED */ + } + if (bitset(EF_RET_PARAM, e->e_flags)) + { + usrerr("501 Duplicate RET parameter"); + /* NOTREACHED */ + } + e->e_flags |= EF_RET_PARAM; + if (strcasecmp(vp, "hdrs") == 0) + e->e_flags |= EF_NO_BODY_RETN; + else if (strcasecmp(vp, "full") != 0) + { + usrerr("501 Bad argument \"%s\" to RET", vp); + /* NOTREACHED */ + } + } + else + { + usrerr("501 %s parameter unrecognized", kp); + /* NOTREACHED */ + } +} +/* +** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line +** +** Parameters: +** a -- the address corresponding to the To: parameter. +** kp -- the parameter key. +** vp -- the value of that parameter. +** e -- the envelope. +** +** Returns: +** none. +*/ + +void +rcpt_esmtp_args(a, kp, vp, e) + ADDRESS *a; + char *kp; + char *vp; + ENVELOPE *e; +{ + if (strcasecmp(kp, "notify") == 0) + { + char *p; + + if (vp == NULL) + { + usrerr("501 NOTIFY requires a value"); + /* NOTREACHED */ + } + a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); + a->q_flags |= QHASNOTIFY; + if (strcasecmp(vp, "never") == 0) + return; + for (p = vp; p != NULL; vp = p) + { + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + if (strcasecmp(vp, "success") == 0) + a->q_flags |= QPINGONSUCCESS; + else if (strcasecmp(vp, "failure") == 0) + a->q_flags |= QPINGONFAILURE; + else if (strcasecmp(vp, "delay") == 0) + a->q_flags |= QPINGONDELAY; + else + { + usrerr("501 Bad argument \"%s\" to NOTIFY", + vp); + /* NOTREACHED */ + } + } + } + else if (strcasecmp(kp, "orcpt") == 0) + { + if (vp == NULL) + { + usrerr("501 ORCPT requires a value"); + /* NOTREACHED */ + } + if (strchr(vp, ';') == NULL || !xtextok(vp)) + { + usrerr("501 Syntax error in ORCPT parameter value"); + /* NOTREACHED */ + } + if (a->q_orcpt != NULL) + { + usrerr("501 Duplicate ORCPT parameter"); + /* NOTREACHED */ + } + a->q_orcpt = newstr(vp); + } + else + { + usrerr("501 %s parameter unrecognized", kp); + /* NOTREACHED */ + } +} +/* ** PRINTVRFYADDR -- print an entry in the verify queue ** ** Parameters: @@ -878,6 +1120,7 @@ skipword(p, w) ** Prints the appropriate 250 codes. */ +void printvrfyaddr(a, last) register ADDRESS *a; bool last; @@ -905,66 +1148,6 @@ printvrfyaddr(a, last) } } /* -** HELP -- implement the HELP command. -** -** Parameters: -** topic -- the topic we want help for. -** -** Returns: -** none. -** -** Side Effects: -** outputs the help file to message output. -*/ - -help(topic) - char *topic; -{ - register FILE *hf; - int len; - char buf[MAXLINE]; - bool noinfo; - - if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) - { - /* no help */ - errno = 0; - message("502 HELP not implemented"); - return; - } - - if (topic == NULL || *topic == '\0') - topic = "smtp"; - else - makelower(topic); - - len = strlen(topic); - noinfo = TRUE; - - while (fgets(buf, sizeof buf, hf) != NULL) - { - if (strncmp(buf, topic, len) == 0) - { - register char *p; - - p = strchr(buf, '\t'); - if (p == NULL) - p = buf; - else - p++; - fixcrlf(p, TRUE); - message("214-%s", p); - noinfo = FALSE; - } - } - - if (noinfo) - message("504 HELP topic unknown"); - else - message("214 End of HELP info"); - (void) fclose(hf); -} -/* ** RUNINCHILD -- return twice -- once in the child, then in the parent again ** ** Parameters: @@ -978,6 +1161,7 @@ help(topic) ** none. */ +int runinchild(label, e) char *label; register ENVELOPE *e; @@ -989,7 +1173,7 @@ runinchild(label, e) childpid = dofork(); if (childpid < 0) { - syserr("%s: cannot fork", label); + syserr("451 %s: cannot fork", label); return (1); } if (childpid > 0) @@ -997,12 +1181,12 @@ runinchild(label, e) auto int st; /* parent -- wait for child to complete */ - setproctitle("server %s child wait", CurHostName); + setproctitle("server %s child wait", CurSmtpClient); st = waitfor(childpid); if (st == -1) - syserr("%s: lost child", label); + syserr("451 %s: lost child", label); else if (!WIFEXITED(st)) - syserr("%s: died on signal %d", + syserr("451 %s: died on signal %d", label, st & 0177); /* if we exited on a QUIT command, complete the process */ @@ -1030,3 +1214,72 @@ runinchild(label, e) } # endif /* SMTP */ +/* +** HELP -- implement the HELP command. +** +** Parameters: +** topic -- the topic we want help for. +** +** Returns: +** none. +** +** Side Effects: +** outputs the help file to message output. +*/ + +void +help(topic) + char *topic; +{ + register FILE *hf; + int len; + bool noinfo; + char buf[MAXLINE]; + extern char Version[]; + + + if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) + { + /* no help */ + errno = 0; + message("502 Sendmail %s -- HELP not implemented", Version); + return; + } + + if (topic == NULL || *topic == '\0') + { + topic = "smtp"; + message("214-This is Sendmail version %s", Version); + noinfo = FALSE; + } + else + { + makelower(topic); + noinfo = TRUE; + } + + len = strlen(topic); + + while (fgets(buf, sizeof buf, hf) != NULL) + { + if (strncmp(buf, topic, len) == 0) + { + register char *p; + + p = strchr(buf, '\t'); + if (p == NULL) + p = buf; + else + p++; + fixcrlf(p, TRUE); + message("214-%s", p); + noinfo = FALSE; + } + } + + if (noinfo) + message("504 HELP topic \"%.10s\" unknown", topic); + else + message("214 End of HELP info"); + (void) fclose(hf); +} |
