13e12c5d1SDavid du Colombier #include "common.h"
23e12c5d1SDavid du Colombier #include "smtpd.h"
39a747e4fSDavid du Colombier #include "smtp.h"
4a50688edSDavid du Colombier #include <ctype.h>
5e288d156SDavid du Colombier #include <ip.h>
6a50688edSDavid du Colombier #include <ndb.h>
7dddc47c2SDavid du Colombier #include <mp.h>
8dddc47c2SDavid du Colombier #include <libsec.h>
93ff48bf5SDavid du Colombier #include <auth.h>
106b6b9ac8SDavid du Colombier #include "../smtp/y.tab.h"
113e12c5d1SDavid du Colombier
123e12c5d1SDavid du Colombier char *me;
137dd7cddfSDavid du Colombier char *him="";
147dd7cddfSDavid du Colombier char *dom;
157dd7cddfSDavid du Colombier process *pp;
169a747e4fSDavid du Colombier String *mailer;
179a747e4fSDavid du Colombier NetConnInfo *nci;
183e12c5d1SDavid du Colombier
197dd7cddfSDavid du Colombier int filterstate = ACCEPT;
207dd7cddfSDavid du Colombier int trusted;
217dd7cddfSDavid du Colombier int logged;
227dd7cddfSDavid du Colombier int rejectcount;
2359cc4ca5SDavid du Colombier int hardreject;
243e12c5d1SDavid du Colombier
25c6569576SDavid du Colombier ulong starttime;
26c6569576SDavid du Colombier
273e12c5d1SDavid du Colombier Biobuf bin;
283e12c5d1SDavid du Colombier
293e12c5d1SDavid du Colombier int debug;
30f06c651dSDavid du Colombier int Dflag;
317dd7cddfSDavid du Colombier int fflag;
32e288d156SDavid du Colombier int gflag;
337dd7cddfSDavid du Colombier int rflag;
347dd7cddfSDavid du Colombier int sflag;
353ff48bf5SDavid du Colombier int authenticate;
363ff48bf5SDavid du Colombier int authenticated;
373ff48bf5SDavid du Colombier int passwordinclear;
38679c15e8SDavid du Colombier char *tlscert;
397dd7cddfSDavid du Colombier
4046595261SDavid du Colombier uchar rsysip[IPaddrlen];
4146595261SDavid du Colombier
427dd7cddfSDavid du Colombier List senders;
437dd7cddfSDavid du Colombier List rcvers;
447dd7cddfSDavid du Colombier
455243b8d1SDavid du Colombier char pipbuf[ERRMAX];
462b7fd5adSDavid du Colombier char *piperror;
470c5b33aeSDavid du Colombier
489a747e4fSDavid du Colombier String* mailerpath(char*);
490c5b33aeSDavid du Colombier int pipemsg(int*);
500c5b33aeSDavid du Colombier int rejectcheck(void);
510c5b33aeSDavid du Colombier String* startcmd(void);
527dd7cddfSDavid du Colombier
5368de9c93SDavid du Colombier static void logmsg(char *action);
54*c1dd2601SDavid du Colombier static int delaysecs(void);
5568de9c93SDavid du Colombier
567dd7cddfSDavid du Colombier static int
catchalarm(void * a,char * msg)577dd7cddfSDavid du Colombier catchalarm(void *a, char *msg)
587dd7cddfSDavid du Colombier {
5904c2b4eaSDavid du Colombier int rv;
60f06c651dSDavid du Colombier
617dd7cddfSDavid du Colombier USED(a);
627dd7cddfSDavid du Colombier
63f06c651dSDavid du Colombier /* log alarms but continue */
6404c2b4eaSDavid du Colombier if(strstr(msg, "alarm") != nil){
6504c2b4eaSDavid du Colombier if(senders.first && senders.first->p &&
6604c2b4eaSDavid du Colombier rcvers.first && rcvers.first->p)
670c5b33aeSDavid du Colombier syslog(0, "smtpd", "note: %s->%s: %s",
680c5b33aeSDavid du Colombier s_to_c(senders.first->p),
697dd7cddfSDavid du Colombier s_to_c(rcvers.first->p), msg);
707dd7cddfSDavid du Colombier else
712b7fd5adSDavid du Colombier syslog(0, "smtpd", "note: %s", msg);
7204c2b4eaSDavid du Colombier rv = Atnoterecog;
7304c2b4eaSDavid du Colombier } else
7404c2b4eaSDavid du Colombier rv = Atnoteunknown;
75c6569576SDavid du Colombier if (debug) {
76c6569576SDavid du Colombier seek(2, 0, 2);
77c6569576SDavid du Colombier fprint(2, "caught note: %s\n", msg);
78c6569576SDavid du Colombier }
79f06c651dSDavid du Colombier
80f06c651dSDavid du Colombier /* kill the children if there are any */
81c6569576SDavid du Colombier if(pp && pp->pid > 0) {
82f06c651dSDavid du Colombier syskillpg(pp->pid);
83e541223cSDavid du Colombier /* none can't syskillpg, so try a variant */
84e541223cSDavid du Colombier sleep(500);
85e541223cSDavid du Colombier syskill(pp->pid);
86e541223cSDavid du Colombier }
87f06c651dSDavid du Colombier
88f06c651dSDavid du Colombier return rv;
897dd7cddfSDavid du Colombier }
907dd7cddfSDavid du Colombier
917dd7cddfSDavid du Colombier /* override string error functions to do something reasonable */
927dd7cddfSDavid du Colombier void
s_error(char * f,char * status)937dd7cddfSDavid du Colombier s_error(char *f, char *status)
947dd7cddfSDavid du Colombier {
959a747e4fSDavid du Colombier char errbuf[Errlen];
967dd7cddfSDavid du Colombier
977dd7cddfSDavid du Colombier errbuf[0] = 0;
989a747e4fSDavid du Colombier rerrstr(errbuf, sizeof(errbuf));
997dd7cddfSDavid du Colombier if(f && *f)
1000c5b33aeSDavid du Colombier reply("452 4.3.0 out of memory %s: %s\r\n", f, errbuf);
1017dd7cddfSDavid du Colombier else
1020c5b33aeSDavid du Colombier reply("452 4.3.0 out of memory %s\r\n", errbuf);
1039a747e4fSDavid du Colombier syslog(0, "smtpd", "++Malloc failure %s [%s]", him, nci->rsys);
1047dd7cddfSDavid du Colombier exits(status);
1057dd7cddfSDavid du Colombier }
1063e12c5d1SDavid du Colombier
1070c5b33aeSDavid du Colombier static void
usage(void)1080c5b33aeSDavid du Colombier usage(void)
1090c5b33aeSDavid du Colombier {
1100c5b33aeSDavid du Colombier fprint(2,
1110c5b33aeSDavid du Colombier "usage: smtpd [-adDfghprs] [-c cert] [-k ip] [-m mailer] [-n net]\n");
1120c5b33aeSDavid du Colombier exits("usage");
1130c5b33aeSDavid du Colombier }
1140c5b33aeSDavid du Colombier
1153e12c5d1SDavid du Colombier void
main(int argc,char ** argv)1163e12c5d1SDavid du Colombier main(int argc, char **argv)
1173e12c5d1SDavid du Colombier {
1189a747e4fSDavid du Colombier char *netdir;
1190c5b33aeSDavid du Colombier char buf[1024];
1207dd7cddfSDavid du Colombier
1219a747e4fSDavid du Colombier netdir = nil;
1222b7fd5adSDavid du Colombier quotefmtinstall();
12346595261SDavid du Colombier fmtinstall('I', eipfmt);
124c6569576SDavid du Colombier starttime = time(0);
1253e12c5d1SDavid du Colombier ARGBEGIN{
1260c5b33aeSDavid du Colombier case 'a':
1270c5b33aeSDavid du Colombier authenticate = 1;
1280c5b33aeSDavid du Colombier break;
1290c5b33aeSDavid du Colombier case 'c':
1300c5b33aeSDavid du Colombier tlscert = EARGF(usage());
1310c5b33aeSDavid du Colombier break;
132f06c651dSDavid du Colombier case 'D':
133f06c651dSDavid du Colombier Dflag++;
134f06c651dSDavid du Colombier break;
1353e12c5d1SDavid du Colombier case 'd':
1363e12c5d1SDavid du Colombier debug++;
1373e12c5d1SDavid du Colombier break;
1387dd7cddfSDavid du Colombier case 'f': /* disallow relaying */
1397dd7cddfSDavid du Colombier fflag = 1;
1407dd7cddfSDavid du Colombier break;
141e288d156SDavid du Colombier case 'g':
142e288d156SDavid du Colombier gflag = 1;
143e288d156SDavid du Colombier break;
1447dd7cddfSDavid du Colombier case 'h': /* default domain name */
1450c5b33aeSDavid du Colombier dom = EARGF(usage());
1467dd7cddfSDavid du Colombier break;
1477dd7cddfSDavid du Colombier case 'k': /* prohibited ip address */
1480c5b33aeSDavid du Colombier addbadguy(EARGF(usage()));
1497dd7cddfSDavid du Colombier break;
1509a747e4fSDavid du Colombier case 'm': /* set mail command */
1510c5b33aeSDavid du Colombier mailer = mailerpath(EARGF(usage()));
1520c5b33aeSDavid du Colombier break;
1530c5b33aeSDavid du Colombier case 'n': /* log peer ip address */
1540c5b33aeSDavid du Colombier netdir = EARGF(usage());
1550c5b33aeSDavid du Colombier break;
1560c5b33aeSDavid du Colombier case 'p':
1570c5b33aeSDavid du Colombier passwordinclear = 1;
1589a747e4fSDavid du Colombier break;
1597dd7cddfSDavid du Colombier case 'r':
1607dd7cddfSDavid du Colombier rflag = 1; /* verify sender's domain */
1617dd7cddfSDavid du Colombier break;
1627dd7cddfSDavid du Colombier case 's': /* save blocked messages */
1637dd7cddfSDavid du Colombier sflag = 1;
1647dd7cddfSDavid du Colombier break;
1650827824dSDavid du Colombier case 't':
1660c5b33aeSDavid du Colombier fprint(2, "%s: the -t option is no longer supported, see -c\n",
1670c5b33aeSDavid du Colombier argv0);
168679c15e8SDavid du Colombier tlscert = "/sys/lib/ssl/smtpd-cert.pem";
1690827824dSDavid du Colombier break;
1703e12c5d1SDavid du Colombier default:
1710c5b33aeSDavid du Colombier usage();
1729a747e4fSDavid du Colombier }ARGEND;
1739a747e4fSDavid du Colombier
1749a747e4fSDavid du Colombier nci = getnetconninfo(netdir, 0);
1759a747e4fSDavid du Colombier if(nci == nil)
176d2f5da3fSDavid du Colombier sysfatal("can't get remote system's address: %r");
17746595261SDavid du Colombier parseip(rsysip, nci->rsys);
1789a747e4fSDavid du Colombier
1799a747e4fSDavid du Colombier if(mailer == nil)
1809a747e4fSDavid du Colombier mailer = mailerpath("send");
1819a747e4fSDavid du Colombier
1823e12c5d1SDavid du Colombier if(debug){
183c6569576SDavid du Colombier snprint(buf, sizeof buf, "%s/smtpdb/%ld", UPASLOG, time(0));
1843e12c5d1SDavid du Colombier close(2);
185c6569576SDavid du Colombier if (create(buf, OWRITE | OEXCL, 0662) >= 0) {
1867dd7cddfSDavid du Colombier seek(2, 0, 2);
1873e12c5d1SDavid du Colombier fprint(2, "%d smtpd %s\n", getpid(), thedate());
1887dd7cddfSDavid du Colombier } else
1897dd7cddfSDavid du Colombier debug = 0;
1903e12c5d1SDavid du Colombier }
1919a747e4fSDavid du Colombier getconf();
192871e55a5SDavid du Colombier if (isbadguy())
193871e55a5SDavid du Colombier exits("banned");
1949a747e4fSDavid du Colombier Binit(&bin, 0, OREAD);
1953e12c5d1SDavid du Colombier
196ea58ad6fSDavid du Colombier if (chdir(UPASLOG) < 0)
197ea58ad6fSDavid du Colombier syslog(0, "smtpd", "no %s: %r", UPASLOG);
1983e12c5d1SDavid du Colombier me = sysname_read();
1997dd7cddfSDavid du Colombier if(dom == 0 || dom[0] == 0)
2007dd7cddfSDavid du Colombier dom = domainname_read();
2017dd7cddfSDavid du Colombier if(dom == 0 || dom[0] == 0)
2027dd7cddfSDavid du Colombier dom = me;
2033e12c5d1SDavid du Colombier parseinit();
204*c1dd2601SDavid du Colombier sayhi();
2050c5b33aeSDavid du Colombier
2067dd7cddfSDavid du Colombier /* allow 45 minutes to parse the header */
2077dd7cddfSDavid du Colombier atnotify(catchalarm, 1);
2087dd7cddfSDavid du Colombier alarm(45*60*1000);
2099a747e4fSDavid du Colombier zzparse();
2103e12c5d1SDavid du Colombier exits(0);
2113e12c5d1SDavid du Colombier }
2123e12c5d1SDavid du Colombier
2137dd7cddfSDavid du Colombier void
listfree(List * l)2143e12c5d1SDavid du Colombier listfree(List *l)
2153e12c5d1SDavid du Colombier {
2160c5b33aeSDavid du Colombier Link *lp, *next;
2173e12c5d1SDavid du Colombier
2183e12c5d1SDavid du Colombier for(lp = l->first; lp; lp = next){
2193e12c5d1SDavid du Colombier next = lp->next;
2203e12c5d1SDavid du Colombier s_free(lp->p);
2213e12c5d1SDavid du Colombier free(lp);
2223e12c5d1SDavid du Colombier }
2233e12c5d1SDavid du Colombier l->first = l->last = 0;
2243e12c5d1SDavid du Colombier }
2253e12c5d1SDavid du Colombier
2267dd7cddfSDavid du Colombier void
listadd(List * l,String * path)2273e12c5d1SDavid du Colombier listadd(List *l, String *path)
2283e12c5d1SDavid du Colombier {
2293e12c5d1SDavid du Colombier Link *lp;
2303e12c5d1SDavid du Colombier
2310c5b33aeSDavid du Colombier lp = (Link *)malloc(sizeof *lp);
2323e12c5d1SDavid du Colombier lp->p = path;
2333e12c5d1SDavid du Colombier lp->next = 0;
2343e12c5d1SDavid du Colombier
2353e12c5d1SDavid du Colombier if(l->last)
2363e12c5d1SDavid du Colombier l->last->next = lp;
2373e12c5d1SDavid du Colombier else
2383e12c5d1SDavid du Colombier l->first = lp;
2393e12c5d1SDavid du Colombier l->last = lp;
2403e12c5d1SDavid du Colombier }
2413e12c5d1SDavid du Colombier
242c6569576SDavid du Colombier void
stamp(void)243c6569576SDavid du Colombier stamp(void)
244c6569576SDavid du Colombier {
245c6569576SDavid du Colombier if(debug) {
246c6569576SDavid du Colombier seek(2, 0, 2);
247c6569576SDavid du Colombier fprint(2, "%3lud ", time(0) - starttime);
248c6569576SDavid du Colombier }
249c6569576SDavid du Colombier }
250c6569576SDavid du Colombier
251219b2ee8SDavid du Colombier #define SIZE 4096
2520c5b33aeSDavid du Colombier
253219b2ee8SDavid du Colombier int
reply(char * fmt,...)254219b2ee8SDavid du Colombier reply(char *fmt, ...)
255219b2ee8SDavid du Colombier {
256e541223cSDavid du Colombier long n;
257219b2ee8SDavid du Colombier char buf[SIZE], *out;
2587dd7cddfSDavid du Colombier va_list arg;
259219b2ee8SDavid du Colombier
2607dd7cddfSDavid du Colombier va_start(arg, fmt);
2619a747e4fSDavid du Colombier out = vseprint(buf, buf+SIZE, fmt, arg);
2627dd7cddfSDavid du Colombier va_end(arg);
2630c5b33aeSDavid du Colombier
264e541223cSDavid du Colombier n = out - buf;
2657dd7cddfSDavid du Colombier if(debug) {
2667dd7cddfSDavid du Colombier seek(2, 0, 2);
267c6569576SDavid du Colombier stamp();
268219b2ee8SDavid du Colombier write(2, buf, n);
2697dd7cddfSDavid du Colombier }
270219b2ee8SDavid du Colombier write(1, buf, n);
271219b2ee8SDavid du Colombier return n;
272219b2ee8SDavid du Colombier }
273219b2ee8SDavid du Colombier
2743e12c5d1SDavid du Colombier void
reset(void)2753e12c5d1SDavid du Colombier reset(void)
2763e12c5d1SDavid du Colombier {
27759cc4ca5SDavid du Colombier if(rejectcheck())
27859cc4ca5SDavid du Colombier return;
2797dd7cddfSDavid du Colombier listfree(&rcvers);
2807dd7cddfSDavid du Colombier listfree(&senders);
2817dd7cddfSDavid du Colombier if(filterstate != DIALUP){
2827dd7cddfSDavid du Colombier logged = 0;
2837dd7cddfSDavid du Colombier filterstate = ACCEPT;
2847dd7cddfSDavid du Colombier }
2850c5b33aeSDavid du Colombier reply("250 2.0.0 ok\r\n");
2863e12c5d1SDavid du Colombier }
2873e12c5d1SDavid du Colombier
2883e12c5d1SDavid du Colombier void
sayhi(void)2893e12c5d1SDavid du Colombier sayhi(void)
2903e12c5d1SDavid du Colombier {
291*c1dd2601SDavid du Colombier Dir *dp;
292*c1dd2601SDavid du Colombier
293*c1dd2601SDavid du Colombier reply("220-%s ESMTP\r\n", dom);
294*c1dd2601SDavid du Colombier sleep(3000);
295*c1dd2601SDavid du Colombier dp = dirfstat(0);
296*c1dd2601SDavid du Colombier if (dp && dp->length > 0) {
297*c1dd2601SDavid du Colombier syslog(0, "smtpd", "Hung up on impatient spammer %s", nci->rsys);
298*c1dd2601SDavid du Colombier if(Dflag)
299*c1dd2601SDavid du Colombier sleep(delaysecs()*1000);
300*c1dd2601SDavid du Colombier reply("554 5.7.0 Spammer!\r\n");
301*c1dd2601SDavid du Colombier exits("spammer didn't wait for greeting to finish");
302*c1dd2601SDavid du Colombier }
303*c1dd2601SDavid du Colombier free(dp);
304*c1dd2601SDavid du Colombier reply("220 \r\n");
3053e12c5d1SDavid du Colombier }
3063e12c5d1SDavid du Colombier
30746595261SDavid du Colombier /*
30846595261SDavid du Colombier * make callers from class A networks infested by spammers
30946595261SDavid du Colombier * wait longer.
31046595261SDavid du Colombier */
31146595261SDavid du Colombier
31246595261SDavid du Colombier static char netaspam[256] = {
31346595261SDavid du Colombier [58] 1,
31446595261SDavid du Colombier [66] 1,
31546595261SDavid du Colombier [71] 1,
31646595261SDavid du Colombier
31746595261SDavid du Colombier [76] 1,
31846595261SDavid du Colombier [77] 1,
31946595261SDavid du Colombier [78] 1,
32046595261SDavid du Colombier [79] 1,
32146595261SDavid du Colombier [80] 1,
32246595261SDavid du Colombier [81] 1,
32346595261SDavid du Colombier [82] 1,
32446595261SDavid du Colombier [83] 1,
32546595261SDavid du Colombier [84] 1,
32646595261SDavid du Colombier [85] 1,
32746595261SDavid du Colombier [86] 1,
32846595261SDavid du Colombier [87] 1,
32946595261SDavid du Colombier [88] 1,
33046595261SDavid du Colombier [89] 1,
33146595261SDavid du Colombier
33246595261SDavid du Colombier [190] 1,
33346595261SDavid du Colombier [201] 1,
33446595261SDavid du Colombier [217] 1,
33546595261SDavid du Colombier };
33646595261SDavid du Colombier
33746595261SDavid du Colombier static int
delaysecs(void)33846595261SDavid du Colombier delaysecs(void)
33946595261SDavid du Colombier {
34039dc1420SDavid du Colombier if (trusted)
34139dc1420SDavid du Colombier return 0;
34239dc1420SDavid du Colombier if (0 && netaspam[rsysip[0]])
343d2f5da3fSDavid du Colombier return 20;
344d2f5da3fSDavid du Colombier return 12;
34546595261SDavid du Colombier }
34646595261SDavid du Colombier
3473e12c5d1SDavid du Colombier void
hello(String * himp,int extended)3483ff48bf5SDavid du Colombier hello(String *himp, int extended)
3493e12c5d1SDavid du Colombier {
3502b7fd5adSDavid du Colombier char **mynames;
3510c5b33aeSDavid du Colombier char *ldot, *rdot;
3522b7fd5adSDavid du Colombier
3535243b8d1SDavid du Colombier him = s_to_c(himp);
3540c5b33aeSDavid du Colombier syslog(0, "smtpd", "%s from %s as %s", extended? "ehlo": "helo",
3550c5b33aeSDavid du Colombier nci->rsys, him);
35659cc4ca5SDavid du Colombier if(rejectcheck())
35759cc4ca5SDavid du Colombier return;
3582b7fd5adSDavid du Colombier
3590c5b33aeSDavid du Colombier if (strchr(him, '.') && nci && !trusted && fflag &&
3600c5b33aeSDavid du Colombier strcmp(nci->rsys, nci->lsys) != 0){
3612b7fd5adSDavid du Colombier /*
3622b7fd5adSDavid du Colombier * We don't care if he lies about who he is, but it is
3632b7fd5adSDavid du Colombier * not okay to pretend to be us. Many viruses do this,
3642b7fd5adSDavid du Colombier * just parroting back what we say in the greeting.
3652b7fd5adSDavid du Colombier */
3662b7fd5adSDavid du Colombier if(strcmp(him, dom) == 0)
3672b7fd5adSDavid du Colombier goto Liarliar;
3682b7fd5adSDavid du Colombier for(mynames = sysnames_read(); mynames && *mynames; mynames++){
3692b7fd5adSDavid du Colombier if(cistrcmp(*mynames, him) == 0){
3702b7fd5adSDavid du Colombier Liarliar:
3710c5b33aeSDavid du Colombier syslog(0, "smtpd",
3720c5b33aeSDavid du Colombier "Hung up on %s; claimed to be %s",
3732b7fd5adSDavid du Colombier nci->rsys, him);
374e4dddb8fSDavid du Colombier if(Dflag)
375e4dddb8fSDavid du Colombier sleep(delaysecs()*1000);
3760c5b33aeSDavid du Colombier reply("554 5.7.0 Liar!\r\n");
3772b7fd5adSDavid du Colombier exits("client pretended to be us");
3782b7fd5adSDavid du Colombier return;
3792b7fd5adSDavid du Colombier }
3802b7fd5adSDavid du Colombier }
3812b7fd5adSDavid du Colombier }
3820c5b33aeSDavid du Colombier
383e5b1f7ddSDavid du Colombier /*
3840c5b33aeSDavid du Colombier * it is unacceptable to claim any string that doesn't look like
3850c5b33aeSDavid du Colombier * a domain name (e.g., has at least one dot in it), but
38653b9a848SDavid du Colombier * Microsoft mail client software gets this wrong, so let trusted
3870c5b33aeSDavid du Colombier * (local) clients omit the dot.
388e5b1f7ddSDavid du Colombier */
3890c5b33aeSDavid du Colombier rdot = strrchr(him, '.');
3900c5b33aeSDavid du Colombier if (rdot && rdot[1] == '\0') {
3910c5b33aeSDavid du Colombier *rdot = '\0'; /* clobber trailing dot */
3920c5b33aeSDavid du Colombier rdot = strrchr(him, '.'); /* try again */
3930c5b33aeSDavid du Colombier }
3940c5b33aeSDavid du Colombier if (!trusted && rdot == nil)
395e5b1f7ddSDavid du Colombier goto Liarliar;
396f55247a6SDavid du Colombier /*
3970c5b33aeSDavid du Colombier * Reject obviously bogus domains and those reserved by RFC 2606.
3980c5b33aeSDavid du Colombier */
3990c5b33aeSDavid du Colombier if (rdot == nil)
4000c5b33aeSDavid du Colombier rdot = him;
4010c5b33aeSDavid du Colombier else
4020c5b33aeSDavid du Colombier rdot++;
4032f2115d0SDavid du Colombier if (!trusted && (cistrcmp(rdot, "localdomain") == 0 ||
4040c5b33aeSDavid du Colombier cistrcmp(rdot, "localhost") == 0 ||
4050c5b33aeSDavid du Colombier cistrcmp(rdot, "example") == 0 ||
4060c5b33aeSDavid du Colombier cistrcmp(rdot, "invalid") == 0 ||
4072f2115d0SDavid du Colombier cistrcmp(rdot, "test") == 0))
4080c5b33aeSDavid du Colombier goto Liarliar; /* bad top-level domain */
4090c5b33aeSDavid du Colombier /* check second-level RFC 2606 domains: example\.(com|net|org) */
4100c5b33aeSDavid du Colombier if (rdot != him)
4110c5b33aeSDavid du Colombier *--rdot = '\0';
4120c5b33aeSDavid du Colombier ldot = strrchr(him, '.');
4130c5b33aeSDavid du Colombier if (rdot != him)
4140c5b33aeSDavid du Colombier *rdot = '.';
4150c5b33aeSDavid du Colombier if (ldot == nil)
4160c5b33aeSDavid du Colombier ldot = him;
4170c5b33aeSDavid du Colombier else
4180c5b33aeSDavid du Colombier ldot++;
4190c5b33aeSDavid du Colombier if (cistrcmp(ldot, "example.com") == 0 ||
4200c5b33aeSDavid du Colombier cistrcmp(ldot, "example.net") == 0 ||
4210c5b33aeSDavid du Colombier cistrcmp(ldot, "example.org") == 0)
4220c5b33aeSDavid du Colombier goto Liarliar;
4230c5b33aeSDavid du Colombier
4240c5b33aeSDavid du Colombier /*
425f55247a6SDavid du Colombier * similarly, if the claimed domain is not an address-literal,
426f55247a6SDavid du Colombier * require at least one letter, which there will be in
427f55247a6SDavid du Colombier * at least the last component (e.g., .com, .net) if it's real.
428f55247a6SDavid du Colombier * this rejects non-address-literal IP addresses,
429f55247a6SDavid du Colombier * among other bogosities.
430f55247a6SDavid du Colombier */
431f55247a6SDavid du Colombier if (!trusted && him[0] != '[') {
432f55247a6SDavid du Colombier char *p;
433f55247a6SDavid du Colombier
434f55247a6SDavid du Colombier for (p = him; *p != '\0'; p++)
435f55247a6SDavid du Colombier if (isascii(*p) && isalpha(*p))
436f55247a6SDavid du Colombier break;
437f55247a6SDavid du Colombier if (*p == '\0')
438f55247a6SDavid du Colombier goto Liarliar;
439f55247a6SDavid du Colombier }
4409a747e4fSDavid du Colombier if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil)
4419a747e4fSDavid du Colombier him = nci->rsys;
4429a747e4fSDavid du Colombier
443f06c651dSDavid du Colombier if(Dflag)
44446595261SDavid du Colombier sleep(delaysecs()*1000);
4453ff48bf5SDavid du Colombier reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him);
4463ff48bf5SDavid du Colombier if (extended) {
4470c5b33aeSDavid du Colombier reply("250-ENHANCEDSTATUSCODES\r\n"); /* RFCs 2034 and 3463 */
448679c15e8SDavid du Colombier if(tlscert != nil)
449dddc47c2SDavid du Colombier reply("250-STARTTLS\r\n");
4503ff48bf5SDavid du Colombier if (passwordinclear)
451d9306527SDavid du Colombier reply("250 AUTH CRAM-MD5 PLAIN LOGIN\r\n");
4523ff48bf5SDavid du Colombier else
4533ff48bf5SDavid du Colombier reply("250 AUTH CRAM-MD5\r\n");
4543ff48bf5SDavid du Colombier }
455219b2ee8SDavid du Colombier }
456219b2ee8SDavid du Colombier
4573e12c5d1SDavid du Colombier void
sender(String * path)4583e12c5d1SDavid du Colombier sender(String *path)
4593e12c5d1SDavid du Colombier {
4607dd7cddfSDavid du Colombier String *s;
4617dd7cddfSDavid du Colombier static char *lastsender;
4627dd7cddfSDavid du Colombier
46359cc4ca5SDavid du Colombier if(rejectcheck())
46459cc4ca5SDavid du Colombier return;
4653ff48bf5SDavid du Colombier if (authenticate && !authenticated) {
4663ff48bf5SDavid du Colombier rejectcount++;
4670c5b33aeSDavid du Colombier reply("530 5.7.0 Authentication required\r\n");
4683ff48bf5SDavid du Colombier return;
4693ff48bf5SDavid du Colombier }
4707dd7cddfSDavid du Colombier if(him == 0 || *him == 0){
4717dd7cddfSDavid du Colombier rejectcount++;
472219b2ee8SDavid du Colombier reply("503 Start by saying HELO, please.\r\n", s_to_c(path));
473219b2ee8SDavid du Colombier return;
474219b2ee8SDavid du Colombier }
4750827824dSDavid du Colombier
4760827824dSDavid du Colombier /* don't add the domain onto black holes or we will loop */
4770827824dSDavid du Colombier if(strchr(s_to_c(path), '!') == 0 && strcmp(s_to_c(path), "/dev/null") != 0){
4787dd7cddfSDavid du Colombier s = s_new();
4797dd7cddfSDavid du Colombier s_append(s, him);
4807dd7cddfSDavid du Colombier s_append(s, "!");
4817dd7cddfSDavid du Colombier s_append(s, s_to_c(path));
4827dd7cddfSDavid du Colombier s_terminate(s);
4837dd7cddfSDavid du Colombier s_free(path);
4847dd7cddfSDavid du Colombier path = s;
4857dd7cddfSDavid du Colombier }
4867dd7cddfSDavid du Colombier if(shellchars(s_to_c(path))){
4877dd7cddfSDavid du Colombier rejectcount++;
4880c5b33aeSDavid du Colombier reply("501 5.1.3 Bad character in sender address %s.\r\n",
4890c5b33aeSDavid du Colombier s_to_c(path));
4903e12c5d1SDavid du Colombier return;
4913e12c5d1SDavid du Colombier }
4927dd7cddfSDavid du Colombier
4937dd7cddfSDavid du Colombier /*
4947dd7cddfSDavid du Colombier * if the last sender address resulted in a rejection because the sending
4950c5b33aeSDavid du Colombier * domain didn't exist and this sender has the same domain, reject
4960c5b33aeSDavid du Colombier * immediately.
4977dd7cddfSDavid du Colombier */
4987dd7cddfSDavid du Colombier if(lastsender){
4997dd7cddfSDavid du Colombier if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){
5007dd7cddfSDavid du Colombier filterstate = REFUSED;
5017dd7cddfSDavid du Colombier rejectcount++;
5020c5b33aeSDavid du Colombier reply("554 5.1.8 Sender domain must exist: %s\r\n",
5030c5b33aeSDavid du Colombier s_to_c(path));
5047dd7cddfSDavid du Colombier return;
5057dd7cddfSDavid du Colombier }
5067dd7cddfSDavid du Colombier free(lastsender); /* different sender domain */
5077dd7cddfSDavid du Colombier lastsender = 0;
5087dd7cddfSDavid du Colombier }
5097dd7cddfSDavid du Colombier
5107dd7cddfSDavid du Colombier /*
5117dd7cddfSDavid du Colombier * see if this ip address, domain name, user name or account is blocked
5127dd7cddfSDavid du Colombier */
5137dd7cddfSDavid du Colombier logged = 0;
51468de9c93SDavid du Colombier filterstate = blocked(path);
51568de9c93SDavid du Colombier /*
51668de9c93SDavid du Colombier * permanently reject what we can before trying smtp ping, which
51768de9c93SDavid du Colombier * often leads to merely temporary rejections.
51868de9c93SDavid du Colombier */
51968de9c93SDavid du Colombier switch (filterstate){
52068de9c93SDavid du Colombier case DENIED:
52168de9c93SDavid du Colombier syslog(0, "smtpd", "Denied %s (%s/%s)",
52268de9c93SDavid du Colombier s_to_c(path), him, nci->rsys);
52368de9c93SDavid du Colombier rejectcount++;
52468de9c93SDavid du Colombier logged++;
52568de9c93SDavid du Colombier reply("554-5.7.1 We don't accept mail from %s.\r\n",
52668de9c93SDavid du Colombier s_to_c(path));
52768de9c93SDavid du Colombier reply("554 5.7.1 Contact postmaster@%s for more information.\r\n",
52868de9c93SDavid du Colombier dom);
52968de9c93SDavid du Colombier return;
53068de9c93SDavid du Colombier case REFUSED:
53168de9c93SDavid du Colombier syslog(0, "smtpd", "Refused %s (%s/%s)",
53268de9c93SDavid du Colombier s_to_c(path), him, nci->rsys);
53368de9c93SDavid du Colombier rejectcount++;
53468de9c93SDavid du Colombier logged++;
53568de9c93SDavid du Colombier reply("554 5.7.1 Sender domain must exist: %s\r\n",
53668de9c93SDavid du Colombier s_to_c(path));
53768de9c93SDavid du Colombier return;
53868de9c93SDavid du Colombier }
53968de9c93SDavid du Colombier
5403e12c5d1SDavid du Colombier listadd(&senders, path);
5410c5b33aeSDavid du Colombier reply("250 2.0.0 sender is %s\r\n", s_to_c(path));
5423e12c5d1SDavid du Colombier }
5433e12c5d1SDavid du Colombier
544a50688edSDavid du Colombier enum { Rcpt, Domain, Ntoks };
545a50688edSDavid du Colombier
546a50688edSDavid du Colombier typedef struct Sender Sender;
547a50688edSDavid du Colombier struct Sender {
548a50688edSDavid du Colombier Sender *next;
549a50688edSDavid du Colombier char *rcpt;
550a50688edSDavid du Colombier char *domain;
551a50688edSDavid du Colombier };
552a50688edSDavid du Colombier static Sender *sendlist, *sendlast;
553a50688edSDavid du Colombier
554a50688edSDavid du Colombier static int
rdsenders(void)555a50688edSDavid du Colombier rdsenders(void)
556a50688edSDavid du Colombier {
557a50688edSDavid du Colombier int lnlen, nf, ok = 1;
558a50688edSDavid du Colombier char *line, *senderfile;
559a50688edSDavid du Colombier char *toks[Ntoks];
560a50688edSDavid du Colombier Biobuf *sf;
561a50688edSDavid du Colombier Sender *snd;
562a50688edSDavid du Colombier static int beenhere = 0;
563a50688edSDavid du Colombier
564a50688edSDavid du Colombier if (beenhere)
565a50688edSDavid du Colombier return 1;
566a50688edSDavid du Colombier beenhere = 1;
567a50688edSDavid du Colombier
568a50688edSDavid du Colombier /*
569a50688edSDavid du Colombier * we're sticking with a system-wide sender list because
570a50688edSDavid du Colombier * per-user lists would require fully resolving recipient
571a50688edSDavid du Colombier * addresses to determine which users they correspond to
57246595261SDavid du Colombier * (barring exploiting syntactic conventions).
573a50688edSDavid du Colombier */
574a50688edSDavid du Colombier senderfile = smprint("%s/senders", UPASLIB);
575a50688edSDavid du Colombier sf = Bopen(senderfile, OREAD);
576a50688edSDavid du Colombier free(senderfile);
577a50688edSDavid du Colombier if (sf == nil)
578a50688edSDavid du Colombier return 1;
579a50688edSDavid du Colombier while ((line = Brdline(sf, '\n')) != nil) {
580a50688edSDavid du Colombier if (line[0] == '#' || line[0] == '\n')
581a50688edSDavid du Colombier continue;
582a50688edSDavid du Colombier lnlen = Blinelen(sf);
583a50688edSDavid du Colombier line[lnlen-1] = '\0'; /* clobber newline */
584a50688edSDavid du Colombier nf = tokenize(line, toks, nelem(toks));
585a50688edSDavid du Colombier if (nf != nelem(toks))
586a50688edSDavid du Colombier continue; /* malformed line */
587a50688edSDavid du Colombier
588a50688edSDavid du Colombier snd = malloc(sizeof *snd);
589a50688edSDavid du Colombier if (snd == nil)
590a50688edSDavid du Colombier sysfatal("out of memory: %r");
591a50688edSDavid du Colombier memset(snd, 0, sizeof *snd);
592a50688edSDavid du Colombier snd->next = nil;
593a50688edSDavid du Colombier
594a50688edSDavid du Colombier if (sendlast == nil)
595a50688edSDavid du Colombier sendlist = snd;
596a50688edSDavid du Colombier else
597a50688edSDavid du Colombier sendlast->next = snd;
598a50688edSDavid du Colombier sendlast = snd;
599a50688edSDavid du Colombier snd->rcpt = strdup(toks[Rcpt]);
600a50688edSDavid du Colombier snd->domain = strdup(toks[Domain]);
601a50688edSDavid du Colombier }
602a50688edSDavid du Colombier Bterm(sf);
603a50688edSDavid du Colombier return ok;
604a50688edSDavid du Colombier }
605a50688edSDavid du Colombier
606a50688edSDavid du Colombier /*
607a50688edSDavid du Colombier * read (recipient, sender's DNS) pairs from /mail/lib/senders.
608a50688edSDavid du Colombier * Only allow mail to recipient from any of sender's IPs.
609a50688edSDavid du Colombier * A recipient not mentioned in the file is always permitted.
610a50688edSDavid du Colombier */
611a50688edSDavid du Colombier static int
senderok(char * rcpt)612a50688edSDavid du Colombier senderok(char *rcpt)
613a50688edSDavid du Colombier {
614a50688edSDavid du Colombier int mentioned = 0, matched = 0;
615a50688edSDavid du Colombier uchar dnsip[IPaddrlen];
616a50688edSDavid du Colombier Sender *snd;
617a50688edSDavid du Colombier Ndbtuple *nt, *next, *first;
618a50688edSDavid du Colombier
619a50688edSDavid du Colombier rdsenders();
620a50688edSDavid du Colombier for (snd = sendlist; snd != nil; snd = snd->next) {
621a50688edSDavid du Colombier if (strcmp(rcpt, snd->rcpt) != 0)
622a50688edSDavid du Colombier continue;
623a50688edSDavid du Colombier /*
624a50688edSDavid du Colombier * see if this domain's ips match nci->rsys.
625a50688edSDavid du Colombier * if not, perhaps a later entry's domain will.
626a50688edSDavid du Colombier */
627a50688edSDavid du Colombier mentioned = 1;
628a50688edSDavid du Colombier if (parseip(dnsip, snd->domain) != -1 &&
629a50688edSDavid du Colombier memcmp(rsysip, dnsip, IPaddrlen) == 0)
630a50688edSDavid du Colombier return 1;
631a50688edSDavid du Colombier /*
632a50688edSDavid du Colombier * NB: nt->line links form a circular list(!).
633a50688edSDavid du Colombier * we need to make one complete pass over it to free it all.
634a50688edSDavid du Colombier */
635a50688edSDavid du Colombier first = nt = dnsquery(nci->root, snd->domain, "ip");
636a50688edSDavid du Colombier if (first == nil)
637a50688edSDavid du Colombier continue;
638a50688edSDavid du Colombier do {
639a50688edSDavid du Colombier if (strcmp(nt->attr, "ip") == 0 &&
640a50688edSDavid du Colombier parseip(dnsip, nt->val) != -1 &&
641a50688edSDavid du Colombier memcmp(rsysip, dnsip, IPaddrlen) == 0)
642a50688edSDavid du Colombier matched = 1;
643a50688edSDavid du Colombier next = nt->line;
644a50688edSDavid du Colombier free(nt);
645a50688edSDavid du Colombier nt = next;
646a50688edSDavid du Colombier } while (nt != first);
647a50688edSDavid du Colombier }
648a50688edSDavid du Colombier if (matched)
649a50688edSDavid du Colombier return 1;
650a50688edSDavid du Colombier else
651a50688edSDavid du Colombier return !mentioned;
652a50688edSDavid du Colombier }
653a50688edSDavid du Colombier
6543e12c5d1SDavid du Colombier void
receiver(String * path)6553e12c5d1SDavid du Colombier receiver(String *path)
6563e12c5d1SDavid du Colombier {
657a50688edSDavid du Colombier char *sender, *rcpt;
65880ee5cbfSDavid du Colombier
65959cc4ca5SDavid du Colombier if(rejectcheck())
66059cc4ca5SDavid du Colombier return;
6617dd7cddfSDavid du Colombier if(him == 0 || *him == 0){
6627dd7cddfSDavid du Colombier rejectcount++;
663219b2ee8SDavid du Colombier reply("503 Start by saying HELO, please\r\n");
6643e12c5d1SDavid du Colombier return;
6653e12c5d1SDavid du Colombier }
6667dd7cddfSDavid du Colombier if(senders.last)
66780ee5cbfSDavid du Colombier sender = s_to_c(senders.last->p);
66880ee5cbfSDavid du Colombier else
66980ee5cbfSDavid du Colombier sender = "<unknown>";
67080ee5cbfSDavid du Colombier
67180ee5cbfSDavid du Colombier if(!recipok(s_to_c(path))){
67280ee5cbfSDavid du Colombier rejectcount++;
6732c7f2648SDavid du Colombier syslog(0, "smtpd",
6742c7f2648SDavid du Colombier "Disallowed %s (%s/%s) to blocked, unknown or invalid name %s",
6759a747e4fSDavid du Colombier sender, him, nci->rsys, s_to_c(path));
6760c5b33aeSDavid du Colombier reply("550 5.1.1 %s ... user unknown\r\n", s_to_c(path));
67780ee5cbfSDavid du Colombier return;
67880ee5cbfSDavid du Colombier }
679a50688edSDavid du Colombier rcpt = s_to_c(path);
680a50688edSDavid du Colombier if (!senderok(rcpt)) {
681a50688edSDavid du Colombier rejectcount++;
682a50688edSDavid du Colombier syslog(0, "smtpd", "Disallowed sending IP of %s (%s/%s) to %s",
683a50688edSDavid du Colombier sender, him, nci->rsys, rcpt);
6840c5b33aeSDavid du Colombier reply("550 5.7.1 %s ... sending system not allowed\r\n", rcpt);
685a50688edSDavid du Colombier return;
686a50688edSDavid du Colombier }
68780ee5cbfSDavid du Colombier
68880ee5cbfSDavid du Colombier logged = 0;
6890c5b33aeSDavid du Colombier
69080ee5cbfSDavid du Colombier /* forwarding() can modify 'path' on loopback request */
6910c5b33aeSDavid du Colombier if(filterstate == ACCEPT && fflag && !authenticated && forwarding(path)) {
6927dd7cddfSDavid du Colombier syslog(0, "smtpd", "Bad Forward %s (%s/%s) (%s)",
693871e55a5SDavid du Colombier senders.last && senders.last->p?
694871e55a5SDavid du Colombier s_to_c(senders.last->p): sender,
6956aadf539SDavid du Colombier him, nci->rsys, path? s_to_c(path): rcpt);
6967dd7cddfSDavid du Colombier rejectcount++;
6970c5b33aeSDavid du Colombier reply("550 5.7.1 we don't relay. send to your-path@[] for "
6980c5b33aeSDavid du Colombier "loopback.\r\n");
6997dd7cddfSDavid du Colombier return;
7007dd7cddfSDavid du Colombier }
7013e12c5d1SDavid du Colombier listadd(&rcvers, path);
7020c5b33aeSDavid du Colombier reply("250 2.0.0 receiver is %s\r\n", s_to_c(path));
7033e12c5d1SDavid du Colombier }
7043e12c5d1SDavid du Colombier
7053e12c5d1SDavid du Colombier void
quit(void)7063e12c5d1SDavid du Colombier quit(void)
7073e12c5d1SDavid du Colombier {
7080c5b33aeSDavid du Colombier reply("221 2.0.0 Successful termination\r\n");
709c6569576SDavid du Colombier if(debug){
710c6569576SDavid du Colombier seek(2, 0, 2);
711c6569576SDavid du Colombier stamp();
712c6569576SDavid du Colombier fprint(2, "# %d sent 221 reply to QUIT %s\n",
713c6569576SDavid du Colombier getpid(), thedate());
714c6569576SDavid du Colombier }
7157dd7cddfSDavid du Colombier close(0);
7163e12c5d1SDavid du Colombier exits(0);
7173e12c5d1SDavid du Colombier }
7183e12c5d1SDavid du Colombier
7193e12c5d1SDavid du Colombier void
noop(void)7203e12c5d1SDavid du Colombier noop(void)
7213e12c5d1SDavid du Colombier {
72259cc4ca5SDavid du Colombier if(rejectcheck())
72359cc4ca5SDavid du Colombier return;
7240c5b33aeSDavid du Colombier reply("250 2.0.0 Nothing to see here. Move along ...\r\n");
7253e12c5d1SDavid du Colombier }
7263e12c5d1SDavid du Colombier
7273e12c5d1SDavid du Colombier void
help(String * cmd)7283e12c5d1SDavid du Colombier help(String *cmd)
7293e12c5d1SDavid du Colombier {
73059cc4ca5SDavid du Colombier if(rejectcheck())
73159cc4ca5SDavid du Colombier return;
7323e12c5d1SDavid du Colombier if(cmd)
7333e12c5d1SDavid du Colombier s_free(cmd);
7340c5b33aeSDavid du Colombier reply("250 2.0.0 See http://www.ietf.org/rfc/rfc2821\r\n");
7353e12c5d1SDavid du Colombier }
7363e12c5d1SDavid du Colombier
7373e12c5d1SDavid du Colombier void
verify(String * path)7383e12c5d1SDavid du Colombier verify(String *path)
7393e12c5d1SDavid du Colombier {
7407dd7cddfSDavid du Colombier char *p, *q;
7417dd7cddfSDavid du Colombier char *av[4];
7423e12c5d1SDavid du Colombier
74359cc4ca5SDavid du Colombier if(rejectcheck())
74459cc4ca5SDavid du Colombier return;
7457dd7cddfSDavid du Colombier if(shellchars(s_to_c(path))){
7460c5b33aeSDavid du Colombier reply("503 5.1.3 Bad character in address %s.\r\n", s_to_c(path));
747219b2ee8SDavid du Colombier return;
748219b2ee8SDavid du Colombier }
7499a747e4fSDavid du Colombier av[0] = s_to_c(mailer);
7507dd7cddfSDavid du Colombier av[1] = "-x";
7517dd7cddfSDavid du Colombier av[2] = s_to_c(path);
7527dd7cddfSDavid du Colombier av[3] = 0;
7533e12c5d1SDavid du Colombier
7547dd7cddfSDavid du Colombier pp = noshell_proc_start(av, (stream *)0, outstream(), (stream *)0, 1, 0);
7553e12c5d1SDavid du Colombier if (pp == 0) {
7560c5b33aeSDavid du Colombier reply("450 4.3.2 We're busy right now, try later\r\n");
7573e12c5d1SDavid du Colombier return;
7583e12c5d1SDavid du Colombier }
7593e12c5d1SDavid du Colombier
7607dd7cddfSDavid du Colombier p = Brdline(pp->std[1]->fp, '\n');
7617dd7cddfSDavid du Colombier if(p == 0){
7620c5b33aeSDavid du Colombier reply("550 5.1.0 String does not match anything.\r\n");
7637dd7cddfSDavid du Colombier } else {
7647dd7cddfSDavid du Colombier p[Blinelen(pp->std[1]->fp)-1] = 0;
7657dd7cddfSDavid du Colombier if(strchr(p, ':'))
7660c5b33aeSDavid du Colombier reply("550 5.1.0 String does not match anything.\r\n");
7677dd7cddfSDavid du Colombier else{
7687dd7cddfSDavid du Colombier q = strrchr(p, '!');
7697dd7cddfSDavid du Colombier if(q)
7707dd7cddfSDavid du Colombier p = q+1;
7710c5b33aeSDavid du Colombier reply("250 2.0.0 %s <%s@%s>\r\n", s_to_c(path), p, dom);
7723e12c5d1SDavid du Colombier }
7733e12c5d1SDavid du Colombier }
7743e12c5d1SDavid du Colombier proc_wait(pp);
7753e12c5d1SDavid du Colombier proc_free(pp);
7767dd7cddfSDavid du Colombier pp = 0;
7773e12c5d1SDavid du Colombier }
7783e12c5d1SDavid du Colombier
7793e12c5d1SDavid du Colombier /*
7803e12c5d1SDavid du Colombier * get a line that ends in crnl or cr, turn terminating crnl into a nl
7813e12c5d1SDavid du Colombier *
7823e12c5d1SDavid du Colombier * return 0 on EOF
7833e12c5d1SDavid du Colombier */
784219b2ee8SDavid du Colombier static int
getcrnl(String * s,Biobuf * fp)7859a747e4fSDavid du Colombier getcrnl(String *s, Biobuf *fp)
7863e12c5d1SDavid du Colombier {
7873e12c5d1SDavid du Colombier int c;
7883e12c5d1SDavid du Colombier
7899a747e4fSDavid du Colombier for(;;){
7903e12c5d1SDavid du Colombier c = Bgetc(fp);
7917dd7cddfSDavid du Colombier if(debug) {
7927dd7cddfSDavid du Colombier seek(2, 0, 2);
793219b2ee8SDavid du Colombier fprint(2, "%c", c);
7947dd7cddfSDavid du Colombier }
7953e12c5d1SDavid du Colombier switch(c){
796efb32250SDavid du Colombier case 0:
797efb32250SDavid du Colombier break;
7983e12c5d1SDavid du Colombier case -1:
7999a747e4fSDavid du Colombier goto out;
8003e12c5d1SDavid du Colombier case '\r':
8013e12c5d1SDavid du Colombier c = Bgetc(fp);
8023e12c5d1SDavid du Colombier if(c == '\n'){
8037dd7cddfSDavid du Colombier if(debug) {
8047dd7cddfSDavid du Colombier seek(2, 0, 2);
805219b2ee8SDavid du Colombier fprint(2, "%c", c);
806c6569576SDavid du Colombier stamp();
8077dd7cddfSDavid du Colombier }
8089a747e4fSDavid du Colombier s_putc(s, '\n');
8099a747e4fSDavid du Colombier goto out;
8103e12c5d1SDavid du Colombier }
8113e12c5d1SDavid du Colombier Bungetc(fp);
8129a747e4fSDavid du Colombier s_putc(s, '\r');
8133e12c5d1SDavid du Colombier break;
814219b2ee8SDavid du Colombier case '\n':
8159a747e4fSDavid du Colombier s_putc(s, c);
8169a747e4fSDavid du Colombier goto out;
8179a747e4fSDavid du Colombier default:
8189a747e4fSDavid du Colombier s_putc(s, c);
8199a747e4fSDavid du Colombier break;
8203e12c5d1SDavid du Colombier }
8213e12c5d1SDavid du Colombier }
8229a747e4fSDavid du Colombier out:
8239a747e4fSDavid du Colombier s_terminate(s);
8249a747e4fSDavid du Colombier return s_len(s);
8253e12c5d1SDavid du Colombier }
8263e12c5d1SDavid du Colombier
8273e12c5d1SDavid du Colombier void
logcall(int nbytes)8287dd7cddfSDavid du Colombier logcall(int nbytes)
8293e12c5d1SDavid du Colombier {
8303e12c5d1SDavid du Colombier Link *l;
8317dd7cddfSDavid du Colombier String *to, *from;
8323e12c5d1SDavid du Colombier
8337dd7cddfSDavid du Colombier to = s_new();
8347dd7cddfSDavid du Colombier from = s_new();
8357dd7cddfSDavid du Colombier for(l = senders.first; l; l = l->next){
8367dd7cddfSDavid du Colombier if(l != senders.first)
8377dd7cddfSDavid du Colombier s_append(from, ", ");
8387dd7cddfSDavid du Colombier s_append(from, s_to_c(l->p));
8393e12c5d1SDavid du Colombier }
8407dd7cddfSDavid du Colombier for(l = rcvers.first; l; l = l->next){
8417dd7cddfSDavid du Colombier if(l != rcvers.first)
8427dd7cddfSDavid du Colombier s_append(to, ", ");
8437dd7cddfSDavid du Colombier s_append(to, s_to_c(l->p));
8447dd7cddfSDavid du Colombier }
8459a747e4fSDavid du Colombier syslog(0, "smtpd", "[%s/%s] %s sent %d bytes to %s", him, nci->rsys,
8467dd7cddfSDavid du Colombier s_to_c(from), nbytes, s_to_c(to));
8477dd7cddfSDavid du Colombier s_free(to);
8487dd7cddfSDavid du Colombier s_free(from);
8493e12c5d1SDavid du Colombier }
8503e12c5d1SDavid du Colombier
8519a747e4fSDavid du Colombier static void
logmsg(char * action)8529a747e4fSDavid du Colombier logmsg(char *action)
8539a747e4fSDavid du Colombier {
8549a747e4fSDavid du Colombier Link *l;
8559a747e4fSDavid du Colombier
8569a747e4fSDavid du Colombier if(logged)
8579a747e4fSDavid du Colombier return;
8589a747e4fSDavid du Colombier
8599a747e4fSDavid du Colombier logged = 1;
8609a747e4fSDavid du Colombier for(l = rcvers.first; l; l = l->next)
8619a747e4fSDavid du Colombier syslog(0, "smtpd", "%s %s (%s/%s) (%s)", action,
8629a747e4fSDavid du Colombier s_to_c(senders.last->p), him, nci->rsys, s_to_c(l->p));
8639a747e4fSDavid du Colombier }
8649a747e4fSDavid du Colombier
865d9306527SDavid du Colombier static int
optoutall(int filterstate)866d9306527SDavid du Colombier optoutall(int filterstate)
867d9306527SDavid du Colombier {
868d9306527SDavid du Colombier Link *l;
869d9306527SDavid du Colombier
870d9306527SDavid du Colombier switch(filterstate){
871d9306527SDavid du Colombier case ACCEPT:
872d9306527SDavid du Colombier case TRUSTED:
873d9306527SDavid du Colombier return filterstate;
874d9306527SDavid du Colombier }
875d9306527SDavid du Colombier
876d9306527SDavid du Colombier for(l = rcvers.first; l; l = l->next)
877d9306527SDavid du Colombier if(!optoutofspamfilter(s_to_c(l->p)))
878d9306527SDavid du Colombier return filterstate;
879d9306527SDavid du Colombier
880d9306527SDavid du Colombier return ACCEPT;
881d9306527SDavid du Colombier }
882d9306527SDavid du Colombier
8837dd7cddfSDavid du Colombier String*
startcmd(void)8847dd7cddfSDavid du Colombier startcmd(void)
8857dd7cddfSDavid du Colombier {
8867dd7cddfSDavid du Colombier int n;
8877dd7cddfSDavid du Colombier char *filename;
8880c5b33aeSDavid du Colombier char **av;
8890c5b33aeSDavid du Colombier Link *l;
8900c5b33aeSDavid du Colombier String *cmd;
8917dd7cddfSDavid du Colombier
892d9306527SDavid du Colombier /*
893d9306527SDavid du Colombier * ignore the filterstate if the all the receivers prefer it.
894d9306527SDavid du Colombier */
895d9306527SDavid du Colombier filterstate = optoutall(filterstate);
896d9306527SDavid du Colombier
8977dd7cddfSDavid du Colombier switch (filterstate){
8987dd7cddfSDavid du Colombier case BLOCKED:
89980ee5cbfSDavid du Colombier case DELAY:
9007dd7cddfSDavid du Colombier rejectcount++;
9019a747e4fSDavid du Colombier logmsg("Blocked");
9027dd7cddfSDavid du Colombier filename = dumpfile(s_to_c(senders.last->p));
9037dd7cddfSDavid du Colombier cmd = s_new();
9047dd7cddfSDavid du Colombier s_append(cmd, "cat > ");
9057dd7cddfSDavid du Colombier s_append(cmd, filename);
9067dd7cddfSDavid du Colombier pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 0, 0);
9077dd7cddfSDavid du Colombier break;
9087dd7cddfSDavid du Colombier case DIALUP:
9099a747e4fSDavid du Colombier logmsg("Dialup");
9107dd7cddfSDavid du Colombier rejectcount++;
9110c5b33aeSDavid du Colombier reply("554 5.7.1 We don't accept mail from dial-up ports.\r\n");
9127dd7cddfSDavid du Colombier /*
9130c5b33aeSDavid du Colombier * we could exit here, because we're never going to accept mail
9140c5b33aeSDavid du Colombier * from this ip address, but it's unclear that RFC821 allows
9150c5b33aeSDavid du Colombier * that. Instead we set the hardreject flag and go stupid.
9167dd7cddfSDavid du Colombier */
91759cc4ca5SDavid du Colombier hardreject = 1;
9187dd7cddfSDavid du Colombier return 0;
9197dd7cddfSDavid du Colombier case DENIED:
9209a747e4fSDavid du Colombier logmsg("Denied");
9217dd7cddfSDavid du Colombier rejectcount++;
9220c5b33aeSDavid du Colombier reply("554-5.7.1 We don't accept mail from %s.\r\n",
9230c5b33aeSDavid du Colombier s_to_c(senders.last->p));
9240c5b33aeSDavid du Colombier reply("554 5.7.1 Contact postmaster@%s for more information.\r\n",
9250c5b33aeSDavid du Colombier dom);
9267dd7cddfSDavid du Colombier return 0;
9277dd7cddfSDavid du Colombier case REFUSED:
9289a747e4fSDavid du Colombier logmsg("Refused");
9297dd7cddfSDavid du Colombier rejectcount++;
9300c5b33aeSDavid du Colombier reply("554 5.7.1 Sender domain must exist: %s\r\n",
9310c5b33aeSDavid du Colombier s_to_c(senders.last->p));
9327dd7cddfSDavid du Colombier return 0;
9337dd7cddfSDavid du Colombier default:
9349a747e4fSDavid du Colombier case NONE:
9359a747e4fSDavid du Colombier logmsg("Confused");
9369a747e4fSDavid du Colombier rejectcount++;
9370c5b33aeSDavid du Colombier reply("554-5.7.0 We have had an internal mailer error "
9380c5b33aeSDavid du Colombier "classifying your message.\r\n");
9390c5b33aeSDavid du Colombier reply("554-5.7.0 Filterstate is %d\r\n", filterstate);
9400c5b33aeSDavid du Colombier reply("554 5.7.0 Contact postmaster@%s for more information.\r\n",
9410c5b33aeSDavid du Colombier dom);
9429a747e4fSDavid du Colombier return 0;
9439a747e4fSDavid du Colombier case ACCEPT:
9443e12c5d1SDavid du Colombier /*
945e288d156SDavid du Colombier * now that all other filters have been passed,
946e288d156SDavid du Colombier * do grey-list processing.
947e288d156SDavid du Colombier */
948e288d156SDavid du Colombier if(gflag)
949e288d156SDavid du Colombier vfysenderhostok();
950d2f5da3fSDavid du Colombier /* fall through */
951e288d156SDavid du Colombier
952d2f5da3fSDavid du Colombier case TRUSTED:
953e288d156SDavid du Colombier /*
9543e12c5d1SDavid du Colombier * set up mail command
9553e12c5d1SDavid du Colombier */
9569a747e4fSDavid du Colombier cmd = s_clone(mailer);
9577dd7cddfSDavid du Colombier n = 3;
9587dd7cddfSDavid du Colombier for(l = rcvers.first; l; l = l->next)
9597dd7cddfSDavid du Colombier n++;
9607dd7cddfSDavid du Colombier av = malloc(n * sizeof(char*));
9617dd7cddfSDavid du Colombier if(av == nil){
9620c5b33aeSDavid du Colombier reply("450 4.3.2 We're busy right now, try later\r\n");
9637dd7cddfSDavid du Colombier s_free(cmd);
9647dd7cddfSDavid du Colombier return 0;
9653e12c5d1SDavid du Colombier }
9663e12c5d1SDavid du Colombier
9677dd7cddfSDavid du Colombier n = 0;
9687dd7cddfSDavid du Colombier av[n++] = s_to_c(cmd);
9697dd7cddfSDavid du Colombier av[n++] = "-r";
9707dd7cddfSDavid du Colombier for(l = rcvers.first; l; l = l->next)
9717dd7cddfSDavid du Colombier av[n++] = s_to_c(l->p);
9727dd7cddfSDavid du Colombier av[n] = 0;
9733e12c5d1SDavid du Colombier /*
9743e12c5d1SDavid du Colombier * start mail process
9753e12c5d1SDavid du Colombier */
9760c5b33aeSDavid du Colombier pp = noshell_proc_start(av, instream(), outstream(),
9770c5b33aeSDavid du Colombier outstream(), 0, 0);
9787dd7cddfSDavid du Colombier free(av);
9797dd7cddfSDavid du Colombier break;
9807dd7cddfSDavid du Colombier }
9813e12c5d1SDavid du Colombier if(pp == 0) {
9820c5b33aeSDavid du Colombier reply("450 4.3.2 We're busy right now, try later\r\n");
9833e12c5d1SDavid du Colombier s_free(cmd);
9847dd7cddfSDavid du Colombier return 0;
9857dd7cddfSDavid du Colombier }
9867dd7cddfSDavid du Colombier return cmd;
9877dd7cddfSDavid du Colombier }
9883e12c5d1SDavid du Colombier
9899a747e4fSDavid du Colombier /*
9909a747e4fSDavid du Colombier * print out a header line, expanding any domainless addresses into
9919a747e4fSDavid du Colombier * address@him
9929a747e4fSDavid du Colombier */
9939a747e4fSDavid du Colombier char*
bprintnode(Biobuf * b,Node * p,int * cntp)99468de9c93SDavid du Colombier bprintnode(Biobuf *b, Node *p, int *cntp)
9959a747e4fSDavid du Colombier {
99668de9c93SDavid du Colombier int len;
99768de9c93SDavid du Colombier
99868de9c93SDavid du Colombier *cntp = 0;
9999a747e4fSDavid du Colombier if(p->s){
10009a747e4fSDavid du Colombier if(p->addr && strchr(s_to_c(p->s), '@') == nil){
10019a747e4fSDavid du Colombier if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
10029a747e4fSDavid du Colombier return nil;
100368de9c93SDavid du Colombier *cntp += s_len(p->s) + 1 + strlen(him);
10049a747e4fSDavid du Colombier } else {
100568de9c93SDavid du Colombier len = s_len(p->s);
100668de9c93SDavid du Colombier if(Bwrite(b, s_to_c(p->s), len) < 0)
10079a747e4fSDavid du Colombier return nil;
100868de9c93SDavid du Colombier *cntp += len;
10099a747e4fSDavid du Colombier }
10109a747e4fSDavid du Colombier }else{
10119a747e4fSDavid du Colombier if(Bputc(b, p->c) < 0)
10129a747e4fSDavid du Colombier return nil;
101368de9c93SDavid du Colombier ++*cntp;
10149a747e4fSDavid du Colombier }
101568de9c93SDavid du Colombier if(p->white) {
101668de9c93SDavid du Colombier len = s_len(p->white);
101768de9c93SDavid du Colombier if(Bwrite(b, s_to_c(p->white), len) < 0)
10189a747e4fSDavid du Colombier return nil;
101968de9c93SDavid du Colombier *cntp += len;
102068de9c93SDavid du Colombier }
10219a747e4fSDavid du Colombier return p->end+1;
10229a747e4fSDavid du Colombier }
10239a747e4fSDavid du Colombier
10246b6b9ac8SDavid du Colombier static String*
getaddr(Node * p)10256b6b9ac8SDavid du Colombier getaddr(Node *p)
10266b6b9ac8SDavid du Colombier {
10276b6b9ac8SDavid du Colombier for(; p; p = p->next)
10286b6b9ac8SDavid du Colombier if(p->s && p->addr)
10296b6b9ac8SDavid du Colombier return p->s;
10306b6b9ac8SDavid du Colombier return nil;
10316b6b9ac8SDavid du Colombier }
10326b6b9ac8SDavid du Colombier
10339a747e4fSDavid du Colombier /*
10340c5b33aeSDavid du Colombier * add warning headers of the form
10350827824dSDavid du Colombier * X-warning: <reason>
10360827824dSDavid du Colombier * for any headers that looked like they might be forged.
10370827824dSDavid du Colombier *
10380827824dSDavid du Colombier * return byte count of new headers
10390827824dSDavid du Colombier */
10400827824dSDavid du Colombier static int
forgedheaderwarnings(void)10410827824dSDavid du Colombier forgedheaderwarnings(void)
10420827824dSDavid du Colombier {
10430827824dSDavid du Colombier int nbytes;
10440827824dSDavid du Colombier Field *f;
10450827824dSDavid du Colombier
10460827824dSDavid du Colombier nbytes = 0;
10470827824dSDavid du Colombier
10480827824dSDavid du Colombier /* warn about envelope sender */
1049c6569576SDavid du Colombier if(senders.last != nil && senders.last->p != nil &&
1050c6569576SDavid du Colombier strcmp(s_to_c(senders.last->p), "/dev/null") != 0 &&
10510c5b33aeSDavid du Colombier masquerade(senders.last->p, nil))
10520c5b33aeSDavid du Colombier nbytes += Bprint(pp->std[0]->fp,
10530c5b33aeSDavid du Colombier "X-warning: suspect envelope domain\n");
10540827824dSDavid du Colombier
10550827824dSDavid du Colombier /*
10560c5b33aeSDavid du Colombier * check Sender: field. If it's OK, ignore the others because this
10570c5b33aeSDavid du Colombier * is an exploded mailing list.
10580827824dSDavid du Colombier */
10590c5b33aeSDavid du Colombier for(f = firstfield; f; f = f->next)
10600c5b33aeSDavid du Colombier if(f->node->c == SENDER)
10610827824dSDavid du Colombier if(masquerade(getaddr(f->node), him))
10620c5b33aeSDavid du Colombier nbytes += Bprint(pp->std[0]->fp,
10630c5b33aeSDavid du Colombier "X-warning: suspect Sender: domain\n");
10640827824dSDavid du Colombier else
10650827824dSDavid du Colombier return nbytes;
10660827824dSDavid du Colombier
10670827824dSDavid du Colombier /* check From: */
10680827824dSDavid du Colombier for(f = firstfield; f; f = f->next){
10690827824dSDavid du Colombier if(f->node->c == FROM && masquerade(getaddr(f->node), him))
10700c5b33aeSDavid du Colombier nbytes += Bprint(pp->std[0]->fp,
10710c5b33aeSDavid du Colombier "X-warning: suspect From: domain\n");
10720827824dSDavid du Colombier }
10730827824dSDavid du Colombier return nbytes;
10740827824dSDavid du Colombier }
10750827824dSDavid du Colombier
10760827824dSDavid du Colombier /*
10779a747e4fSDavid du Colombier * pipe message to mailer with the following transformations:
10789a747e4fSDavid du Colombier * - change \r\n into \n.
10799a747e4fSDavid du Colombier * - add sender's domain to any addrs with no domain
10809a747e4fSDavid du Colombier * - add a From: if none of From:, Sender:, or Replyto: exists
10819a747e4fSDavid du Colombier * - add a Received: line
10829a747e4fSDavid du Colombier */
10837dd7cddfSDavid du Colombier int
pipemsg(int * byteswritten)10847dd7cddfSDavid du Colombier pipemsg(int *byteswritten)
10857dd7cddfSDavid du Colombier {
108668de9c93SDavid du Colombier int n, nbytes, sawdot, status, nonhdr, bpr;
10877dd7cddfSDavid du Colombier char *cp;
10889a747e4fSDavid du Colombier Field *f;
10896b6b9ac8SDavid du Colombier Link *l;
10900c5b33aeSDavid du Colombier Node *p;
10910c5b33aeSDavid du Colombier String *hdr, *line;
10923e12c5d1SDavid du Colombier
10933e12c5d1SDavid du Colombier pipesig(&status); /* set status to 1 on write to closed pipe */
109459cc4ca5SDavid du Colombier sawdot = 0;
10959a747e4fSDavid du Colombier status = 0;
10969a747e4fSDavid du Colombier
10979a747e4fSDavid du Colombier /*
10989a747e4fSDavid du Colombier * add a 'From ' line as envelope
10999a747e4fSDavid du Colombier */
11009a747e4fSDavid du Colombier nbytes = 0;
11019a747e4fSDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
11029a747e4fSDavid du Colombier s_to_c(senders.first->p), thedate());
11039a747e4fSDavid du Colombier
11049a747e4fSDavid du Colombier /*
11059a747e4fSDavid du Colombier * add our own Received: stamp
11069a747e4fSDavid du Colombier */
11079a747e4fSDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
11089a747e4fSDavid du Colombier if(nci->rsys)
11099a747e4fSDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
11109a747e4fSDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
11119a747e4fSDavid du Colombier
11129a747e4fSDavid du Colombier /*
11139a747e4fSDavid du Colombier * read first 16k obeying '.' escape. we're assuming
11149a747e4fSDavid du Colombier * the header will all be there.
11159a747e4fSDavid du Colombier */
11169a747e4fSDavid du Colombier line = s_new();
11179a747e4fSDavid du Colombier hdr = s_new();
11189a747e4fSDavid du Colombier while(sawdot == 0 && s_len(hdr) < 16*1024){
11199a747e4fSDavid du Colombier n = getcrnl(s_reset(line), &bin);
11209a747e4fSDavid du Colombier
11219a747e4fSDavid du Colombier /* eof or error ends the message */
11229a747e4fSDavid du Colombier if(n <= 0)
11239a747e4fSDavid du Colombier break;
11249a747e4fSDavid du Colombier
11259a747e4fSDavid du Colombier /* a line with only a '.' ends the message */
11269a747e4fSDavid du Colombier cp = s_to_c(line);
11279a747e4fSDavid du Colombier if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
112859cc4ca5SDavid du Colombier sawdot = 1;
11293e12c5d1SDavid du Colombier break;
11303e12c5d1SDavid du Colombier }
11319a747e4fSDavid du Colombier
11323ff48bf5SDavid du Colombier s_append(hdr, *cp == '.' ? cp+1 : cp);
113359cc4ca5SDavid du Colombier }
11349a747e4fSDavid du Colombier
11359a747e4fSDavid du Colombier /*
11369a747e4fSDavid du Colombier * parse header
11379a747e4fSDavid du Colombier */
11389a747e4fSDavid du Colombier yyinit(s_to_c(hdr), s_len(hdr));
11399a747e4fSDavid du Colombier yyparse();
11409a747e4fSDavid du Colombier
11419a747e4fSDavid du Colombier /*
11422b7fd5adSDavid du Colombier * Look for masquerades. Let Sender: trump From: to allow mailing list
11430827824dSDavid du Colombier * forwarded messages.
11449a747e4fSDavid du Colombier */
11450827824dSDavid du Colombier if(fflag)
11460827824dSDavid du Colombier nbytes += forgedheaderwarnings();
11476b6b9ac8SDavid du Colombier
11486b6b9ac8SDavid du Colombier /*
11496b6b9ac8SDavid du Colombier * add an orginator and/or destination if either is missing
11506b6b9ac8SDavid du Colombier */
11516b6b9ac8SDavid du Colombier if(originator == 0){
1152c6569576SDavid du Colombier if(senders.last == nil || senders.last->p == nil)
115368de9c93SDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "From: /dev/null@%s\n",
115468de9c93SDavid du Colombier him);
11556b6b9ac8SDavid du Colombier else
115668de9c93SDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "From: %s\n",
11570c5b33aeSDavid du Colombier s_to_c(senders.last->p));
11586b6b9ac8SDavid du Colombier }
11596b6b9ac8SDavid du Colombier if(destination == 0){
116068de9c93SDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "To: ");
11616b6b9ac8SDavid du Colombier for(l = rcvers.first; l; l = l->next){
11626b6b9ac8SDavid du Colombier if(l != rcvers.first)
116368de9c93SDavid du Colombier nbytes += Bprint(pp->std[0]->fp, ", ");
116468de9c93SDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "%s", s_to_c(l->p));
11656b6b9ac8SDavid du Colombier }
116668de9c93SDavid du Colombier nbytes += Bprint(pp->std[0]->fp, "\n");
11676b6b9ac8SDavid du Colombier }
11689a747e4fSDavid du Colombier
11699a747e4fSDavid du Colombier /*
11709a747e4fSDavid du Colombier * add sender's domain to any domainless addresses
11719a747e4fSDavid du Colombier * (to avoid forging local addresses)
11729a747e4fSDavid du Colombier */
11739a747e4fSDavid du Colombier cp = s_to_c(hdr);
11749a747e4fSDavid du Colombier for(f = firstfield; cp != nil && f; f = f->next){
117568de9c93SDavid du Colombier for(p = f->node; cp != 0 && p; p = p->next) {
117668de9c93SDavid du Colombier bpr = 0;
117768de9c93SDavid du Colombier cp = bprintnode(pp->std[0]->fp, p, &bpr);
117868de9c93SDavid du Colombier nbytes += bpr;
117968de9c93SDavid du Colombier }
11802b7fd5adSDavid du Colombier if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
11812b7fd5adSDavid du Colombier piperror = "write error";
1182f06c651dSDavid du Colombier status = 1;
11839a747e4fSDavid du Colombier }
118468de9c93SDavid du Colombier nbytes++; /* for newline */
11852b7fd5adSDavid du Colombier }
11862b7fd5adSDavid du Colombier if(cp == nil){
11872b7fd5adSDavid du Colombier piperror = "sender domain";
11889a747e4fSDavid du Colombier status = 1;
11892b7fd5adSDavid du Colombier }
11909a747e4fSDavid du Colombier
11919a747e4fSDavid du Colombier /* write anything we read following the header */
119268de9c93SDavid du Colombier nonhdr = s_to_c(hdr) + s_len(hdr) - cp;
119368de9c93SDavid du Colombier if(status == 0 && Bwrite(pp->std[0]->fp, cp, nonhdr) < 0){
11942b7fd5adSDavid du Colombier piperror = "write error 2";
11959a747e4fSDavid du Colombier status = 1;
11962b7fd5adSDavid du Colombier }
119768de9c93SDavid du Colombier nbytes += nonhdr;
11989a747e4fSDavid du Colombier s_free(hdr);
11999a747e4fSDavid du Colombier
12009a747e4fSDavid du Colombier /*
12019a747e4fSDavid du Colombier * pass rest of message to mailer. take care of '.'
12029a747e4fSDavid du Colombier * escapes.
12039a747e4fSDavid du Colombier */
1204f06c651dSDavid du Colombier while(sawdot == 0){
12059a747e4fSDavid du Colombier n = getcrnl(s_reset(line), &bin);
12069a747e4fSDavid du Colombier
12079a747e4fSDavid du Colombier /* eof or error ends the message */
12089a747e4fSDavid du Colombier if(n <= 0)
12099a747e4fSDavid du Colombier break;
12109a747e4fSDavid du Colombier
12119a747e4fSDavid du Colombier /* a line with only a '.' ends the message */
12129a747e4fSDavid du Colombier cp = s_to_c(line);
12139a747e4fSDavid du Colombier if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
12149a747e4fSDavid du Colombier sawdot = 1;
12159a747e4fSDavid du Colombier break;
12167dd7cddfSDavid du Colombier }
1217efb32250SDavid du Colombier if(cp[0] == '.'){
1218efb32250SDavid du Colombier cp++;
1219efb32250SDavid du Colombier n--;
1220efb32250SDavid du Colombier }
12217dd7cddfSDavid du Colombier nbytes += n;
1222efb32250SDavid du Colombier if(status == 0 && Bwrite(pp->std[0]->fp, cp, n) < 0){
12232b7fd5adSDavid du Colombier piperror = "write error 3";
12243e12c5d1SDavid du Colombier status = 1;
12253e12c5d1SDavid du Colombier }
12269a747e4fSDavid du Colombier }
12279a747e4fSDavid du Colombier s_free(line);
122859cc4ca5SDavid du Colombier if(sawdot == 0){
122959cc4ca5SDavid du Colombier /* message did not terminate normally */
12305243b8d1SDavid du Colombier snprint(pipbuf, sizeof pipbuf, "network eof: %r");
12315243b8d1SDavid du Colombier piperror = pipbuf;
1232c6569576SDavid du Colombier if (pp->pid > 0) {
123359cc4ca5SDavid du Colombier syskillpg(pp->pid);
1234e541223cSDavid du Colombier /* none can't syskillpg, so try a variant */
1235e541223cSDavid du Colombier sleep(500);
1236e541223cSDavid du Colombier syskill(pp->pid);
1237c6569576SDavid du Colombier }
123859cc4ca5SDavid du Colombier status = 1;
123959cc4ca5SDavid du Colombier }
124059cc4ca5SDavid du Colombier
12412b7fd5adSDavid du Colombier if(status == 0 && Bflush(pp->std[0]->fp) < 0){
12422b7fd5adSDavid du Colombier piperror = "write error 4";
12433e12c5d1SDavid du Colombier status = 1;
12442b7fd5adSDavid du Colombier }
1245c6569576SDavid du Colombier if (debug) {
1246c6569576SDavid du Colombier stamp();
1247c6569576SDavid du Colombier fprint(2, "at end of message; %s .\n",
1248c6569576SDavid du Colombier (sawdot? "saw": "didn't see"));
1249c6569576SDavid du Colombier }
12503e12c5d1SDavid du Colombier stream_free(pp->std[0]);
12513e12c5d1SDavid du Colombier pp->std[0] = 0;
12527dd7cddfSDavid du Colombier *byteswritten = nbytes;
1253f06c651dSDavid du Colombier pipesigoff();
12542b7fd5adSDavid du Colombier if(status && !piperror)
12552b7fd5adSDavid du Colombier piperror = "write on closed pipe";
12567dd7cddfSDavid du Colombier return status;
12577dd7cddfSDavid du Colombier }
12587dd7cddfSDavid du Colombier
1259a50688edSDavid du Colombier char*
firstline(char * x)1260a50688edSDavid du Colombier firstline(char *x)
1261a50688edSDavid du Colombier {
1262a50688edSDavid du Colombier char *p;
12630c5b33aeSDavid du Colombier static char buf[128];
1264a50688edSDavid du Colombier
1265a50688edSDavid du Colombier strncpy(buf, x, sizeof(buf));
1266a50688edSDavid du Colombier buf[sizeof(buf)-1] = 0;
1267a50688edSDavid du Colombier p = strchr(buf, '\n');
1268a50688edSDavid du Colombier if(p)
1269a50688edSDavid du Colombier *p = 0;
1270a50688edSDavid du Colombier return buf;
1271a50688edSDavid du Colombier }
1272a50688edSDavid du Colombier
12732b7fd5adSDavid du Colombier int
sendermxcheck(void)12742b7fd5adSDavid du Colombier sendermxcheck(void)
12752b7fd5adSDavid du Colombier {
12762b7fd5adSDavid du Colombier int pid;
12770c5b33aeSDavid du Colombier char *cp, *senddom, *user, *who;
12782b7fd5adSDavid du Colombier Waitmsg *w;
12792b7fd5adSDavid du Colombier
12802b7fd5adSDavid du Colombier who = s_to_c(senders.first->p);
12812b7fd5adSDavid du Colombier if(strcmp(who, "/dev/null") == 0){
12822b7fd5adSDavid du Colombier /* /dev/null can only send to one rcpt at a time */
12832b7fd5adSDavid du Colombier if(rcvers.first != rcvers.last){
12840c5b33aeSDavid du Colombier werrstr("rejected: /dev/null sending to multiple "
12850c5b33aeSDavid du Colombier "recipients");
12862b7fd5adSDavid du Colombier return -1;
12872b7fd5adSDavid du Colombier }
12882b7fd5adSDavid du Colombier return 0;
12892b7fd5adSDavid du Colombier }
12902b7fd5adSDavid du Colombier
12912b7fd5adSDavid du Colombier if(access("/mail/lib/validatesender", AEXEC) < 0)
12922b7fd5adSDavid du Colombier return 0;
12932b7fd5adSDavid du Colombier
12942b7fd5adSDavid du Colombier senddom = strdup(who);
12952b7fd5adSDavid du Colombier if((cp = strchr(senddom, '!')) == nil){
12962b7fd5adSDavid du Colombier werrstr("rejected: domainless sender %s", who);
12972b7fd5adSDavid du Colombier free(senddom);
12982b7fd5adSDavid du Colombier return -1;
12992b7fd5adSDavid du Colombier }
13002b7fd5adSDavid du Colombier *cp++ = 0;
13012b7fd5adSDavid du Colombier user = cp;
13022b7fd5adSDavid du Colombier
13032b7fd5adSDavid du Colombier switch(pid = fork()){
13042b7fd5adSDavid du Colombier case -1:
13052b7fd5adSDavid du Colombier werrstr("deferred: fork: %r");
13062b7fd5adSDavid du Colombier return -1;
13072b7fd5adSDavid du Colombier case 0:
13082b7fd5adSDavid du Colombier /*
13092b7fd5adSDavid du Colombier * Could add an option with the remote IP address
13102b7fd5adSDavid du Colombier * to allow validatesender to implement SPF eventually.
13112b7fd5adSDavid du Colombier */
13122b7fd5adSDavid du Colombier execl("/mail/lib/validatesender", "validatesender",
13132b7fd5adSDavid du Colombier "-n", nci->root, senddom, user, nil);
13142b7fd5adSDavid du Colombier _exits("exec validatesender: %r");
13152b7fd5adSDavid du Colombier default:
13162b7fd5adSDavid du Colombier break;
13172b7fd5adSDavid du Colombier }
13182b7fd5adSDavid du Colombier
13192b7fd5adSDavid du Colombier free(senddom);
13202b7fd5adSDavid du Colombier w = wait();
13212b7fd5adSDavid du Colombier if(w == nil){
13222b7fd5adSDavid du Colombier werrstr("deferred: wait failed: %r");
13232b7fd5adSDavid du Colombier return -1;
13242b7fd5adSDavid du Colombier }
13252b7fd5adSDavid du Colombier if(w->pid != pid){
13260c5b33aeSDavid du Colombier werrstr("deferred: wait returned wrong pid %d != %d",
13270c5b33aeSDavid du Colombier w->pid, pid);
13282b7fd5adSDavid du Colombier free(w);
13292b7fd5adSDavid du Colombier return -1;
13302b7fd5adSDavid du Colombier }
13312b7fd5adSDavid du Colombier if(w->msg[0] == 0){
13322b7fd5adSDavid du Colombier free(w);
13332b7fd5adSDavid du Colombier return 0;
13342b7fd5adSDavid du Colombier }
13352b7fd5adSDavid du Colombier /*
13362b7fd5adSDavid du Colombier * skip over validatesender 143123132: prefix from rc.
13372b7fd5adSDavid du Colombier */
13382b7fd5adSDavid du Colombier cp = strchr(w->msg, ':');
13392b7fd5adSDavid du Colombier if(cp && *(cp+1) == ' ')
13402b7fd5adSDavid du Colombier werrstr("%s", cp+2);
13412b7fd5adSDavid du Colombier else
13422b7fd5adSDavid du Colombier werrstr("%s", w->msg);
13432b7fd5adSDavid du Colombier free(w);
13442b7fd5adSDavid du Colombier return -1;
13452b7fd5adSDavid du Colombier }
13462b7fd5adSDavid du Colombier
13477dd7cddfSDavid du Colombier void
data(void)13487dd7cddfSDavid du Colombier data(void)
13497dd7cddfSDavid du Colombier {
13507dd7cddfSDavid du Colombier int status, nbytes;
13517dd7cddfSDavid du Colombier char *cp, *ep;
13522b7fd5adSDavid du Colombier char errx[ERRMAX];
13532b7fd5adSDavid du Colombier Link *l;
13540c5b33aeSDavid du Colombier String *cmd, *err;
13557dd7cddfSDavid du Colombier
135659cc4ca5SDavid du Colombier if(rejectcheck())
135759cc4ca5SDavid du Colombier return;
13587dd7cddfSDavid du Colombier if(senders.last == 0){
13590c5b33aeSDavid du Colombier reply("503 2.5.2 Data without MAIL FROM:\r\n");
13607dd7cddfSDavid du Colombier rejectcount++;
13617dd7cddfSDavid du Colombier return;
13627dd7cddfSDavid du Colombier }
13637dd7cddfSDavid du Colombier if(rcvers.last == 0){
13640c5b33aeSDavid du Colombier reply("503 2.5.2 Data without RCPT TO:\r\n");
13657dd7cddfSDavid du Colombier rejectcount++;
13667dd7cddfSDavid du Colombier return;
13677dd7cddfSDavid du Colombier }
1368ee7057f8SDavid du Colombier if(!trusted && sendermxcheck()){
13692b7fd5adSDavid du Colombier rerrstr(errx, sizeof errx);
13702b7fd5adSDavid du Colombier if(strncmp(errx, "rejected:", 9) == 0)
13710c5b33aeSDavid du Colombier reply("554 5.7.1 %s\r\n", errx);
13722b7fd5adSDavid du Colombier else
13730c5b33aeSDavid du Colombier reply("450 4.7.0 %s\r\n", errx);
13742b7fd5adSDavid du Colombier for(l=rcvers.first; l; l=l->next)
13752b7fd5adSDavid du Colombier syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s",
13762b7fd5adSDavid du Colombier him, nci->rsys, s_to_c(senders.first->p),
13772b7fd5adSDavid du Colombier s_to_c(l->p), errx);
13782b7fd5adSDavid du Colombier rejectcount++;
13792b7fd5adSDavid du Colombier return;
13802b7fd5adSDavid du Colombier }
13817dd7cddfSDavid du Colombier
13827dd7cddfSDavid du Colombier cmd = startcmd();
13837dd7cddfSDavid du Colombier if(cmd == 0)
13847dd7cddfSDavid du Colombier return;
13857dd7cddfSDavid du Colombier
13867dd7cddfSDavid du Colombier reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
1387c6569576SDavid du Colombier if(debug){
1388c6569576SDavid du Colombier seek(2, 0, 2);
1389c6569576SDavid du Colombier stamp();
1390c6569576SDavid du Colombier fprint(2, "# sent 354; accepting DATA %s\n", thedate());
1391c6569576SDavid du Colombier }
1392c6569576SDavid du Colombier
13937dd7cddfSDavid du Colombier
13947dd7cddfSDavid du Colombier /*
13953ff48bf5SDavid du Colombier * allow 145 more minutes to move the data
13967dd7cddfSDavid du Colombier */
13973ff48bf5SDavid du Colombier alarm(145*60*1000);
13987dd7cddfSDavid du Colombier
13997dd7cddfSDavid du Colombier status = pipemsg(&nbytes);
14003e12c5d1SDavid du Colombier
14013e12c5d1SDavid du Colombier /*
14023e12c5d1SDavid du Colombier * read any error messages
14033e12c5d1SDavid du Colombier */
1404219b2ee8SDavid du Colombier err = s_new();
1405c6569576SDavid du Colombier if (debug) {
1406c6569576SDavid du Colombier stamp();
1407c6569576SDavid du Colombier fprint(2, "waiting for upas/send to close stderr\n");
1408c6569576SDavid du Colombier }
14093e12c5d1SDavid du Colombier while(s_read_line(pp->std[2]->fp, err))
14103e12c5d1SDavid du Colombier ;
14117dd7cddfSDavid du Colombier
14127dd7cddfSDavid du Colombier alarm(0);
14137dd7cddfSDavid du Colombier atnotify(catchalarm, 0);
14147dd7cddfSDavid du Colombier
1415c6569576SDavid du Colombier if (debug) {
1416c6569576SDavid du Colombier stamp();
1417c6569576SDavid du Colombier fprint(2, "waiting for upas/send to exit\n");
1418c6569576SDavid du Colombier }
14197dd7cddfSDavid du Colombier status |= proc_wait(pp);
14203e12c5d1SDavid du Colombier if(debug){
14217dd7cddfSDavid du Colombier seek(2, 0, 2);
1422c6569576SDavid du Colombier stamp();
1423c6569576SDavid du Colombier fprint(2, "# %d upas/send status %#ux at %s\n",
1424c6569576SDavid du Colombier getpid(), status, thedate());
14253e12c5d1SDavid du Colombier if(*s_to_c(err))
1426c6569576SDavid du Colombier fprint(2, "# %d error %s\n", getpid(), s_to_c(err));
14273e12c5d1SDavid du Colombier }
14283e12c5d1SDavid du Colombier
14293e12c5d1SDavid du Colombier /*
14303e12c5d1SDavid du Colombier * if process terminated abnormally, send back error message
14313e12c5d1SDavid du Colombier */
14323e12c5d1SDavid du Colombier if(status){
1433f06c651dSDavid du Colombier int code;
14340c5b33aeSDavid du Colombier char *ecode;
1435f06c651dSDavid du Colombier
1436f06c651dSDavid du Colombier if(strstr(s_to_c(err), "mail refused")){
14370c5b33aeSDavid du Colombier syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s",
14380c5b33aeSDavid du Colombier him, nci->rsys, s_to_c(senders.first->p),
14390c5b33aeSDavid du Colombier s_to_c(cmd), firstline(s_to_c(err)));
1440f06c651dSDavid du Colombier code = 554;
14410c5b33aeSDavid du Colombier ecode = "5.0.0";
1442f06c651dSDavid du Colombier } else {
14430c5b33aeSDavid du Colombier syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s",
14440c5b33aeSDavid du Colombier him, nci->rsys,
14452b7fd5adSDavid du Colombier s_to_c(senders.first->p), s_to_c(cmd),
14462b7fd5adSDavid du Colombier piperror? "error during pipemsg: ": "",
14472b7fd5adSDavid du Colombier piperror? piperror: "",
14482b7fd5adSDavid du Colombier piperror? "; ": "",
14492b7fd5adSDavid du Colombier pp->waitmsg->msg, firstline(s_to_c(err)));
1450f06c651dSDavid du Colombier code = 450;
14510c5b33aeSDavid du Colombier ecode = "4.0.0";
1452f06c651dSDavid du Colombier }
14533e12c5d1SDavid du Colombier for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
14543e12c5d1SDavid du Colombier *ep++ = 0;
14550c5b33aeSDavid du Colombier reply("%d-%s %s\r\n", code, ecode, cp);
14563e12c5d1SDavid du Colombier }
14570c5b33aeSDavid du Colombier reply("%d %s mail process terminated abnormally\r\n",
14580c5b33aeSDavid du Colombier code, ecode);
14597dd7cddfSDavid du Colombier } else {
1460be704722SDavid du Colombier /*
1461be704722SDavid du Colombier * if a message appeared on stderr, despite good status,
1462be704722SDavid du Colombier * log it. this can happen if rewrite.in contains a bad
1463be704722SDavid du Colombier * r.e., for example.
1464be704722SDavid du Colombier */
1465be704722SDavid du Colombier if(*s_to_c(err))
1466be704722SDavid du Colombier syslog(0, "smtpd",
1467be704722SDavid du Colombier "%s returned good status, but said: %s",
1468be704722SDavid du Colombier s_to_c(mailer), s_to_c(err));
1469be704722SDavid du Colombier
14707dd7cddfSDavid du Colombier if(filterstate == BLOCKED)
14710c5b33aeSDavid du Colombier reply("554 5.7.1 we believe this is spam. "
14720c5b33aeSDavid du Colombier "we don't accept it.\r\n");
14730c5b33aeSDavid du Colombier else if(filterstate == DELAY)
14740c5b33aeSDavid du Colombier reply("450 4.3.0 There will be a delay in delivery "
14750c5b33aeSDavid du Colombier "of this message.\r\n");
147680ee5cbfSDavid du Colombier else {
14770c5b33aeSDavid du Colombier reply("250 2.5.0 sent\r\n");
14787dd7cddfSDavid du Colombier logcall(nbytes);
1479c6569576SDavid du Colombier if(debug){
1480c6569576SDavid du Colombier seek(2, 0, 2);
1481c6569576SDavid du Colombier stamp();
1482c6569576SDavid du Colombier fprint(2, "# %d sent 250 reply %s\n",
1483c6569576SDavid du Colombier getpid(), thedate());
1484c6569576SDavid du Colombier }
14857dd7cddfSDavid du Colombier }
14867dd7cddfSDavid du Colombier }
14872b7fd5adSDavid du Colombier proc_free(pp);
14882b7fd5adSDavid du Colombier pp = 0;
14897dd7cddfSDavid du Colombier s_free(cmd);
1490219b2ee8SDavid du Colombier s_free(err);
14913e12c5d1SDavid du Colombier
1492219b2ee8SDavid du Colombier listfree(&senders);
1493219b2ee8SDavid du Colombier listfree(&rcvers);
14943e12c5d1SDavid du Colombier }
14957dd7cddfSDavid du Colombier
149659cc4ca5SDavid du Colombier /*
149759cc4ca5SDavid du Colombier * when we have blocked a transaction based on IP address, there is nothing
149859cc4ca5SDavid du Colombier * that the sender can do to convince us to take the message. after the
149959cc4ca5SDavid du Colombier * first rejection, some spammers continually RSET and give a new MAIL FROM:
150059cc4ca5SDavid du Colombier * filling our logs with rejections. rejectcheck() limits the retries and
150159cc4ca5SDavid du Colombier * swiftly rejects all further commands after the first 500-series message
150259cc4ca5SDavid du Colombier * is issued.
150359cc4ca5SDavid du Colombier */
150459cc4ca5SDavid du Colombier int
rejectcheck(void)15057dd7cddfSDavid du Colombier rejectcheck(void)
15067dd7cddfSDavid du Colombier {
15077dd7cddfSDavid du Colombier if(rejectcount > MAXREJECTS){
15089a747e4fSDavid du Colombier syslog(0, "smtpd", "Rejected (%s/%s)", him, nci->rsys);
15090c5b33aeSDavid du Colombier reply("554 5.5.0 too many errors. transaction failed.\r\n");
15107dd7cddfSDavid du Colombier exits("errcount");
15117dd7cddfSDavid du Colombier }
151259cc4ca5SDavid du Colombier if(hardreject){
151359cc4ca5SDavid du Colombier rejectcount++;
15140c5b33aeSDavid du Colombier reply("554 5.7.1 We don't accept mail from dial-up ports.\r\n");
151559cc4ca5SDavid du Colombier }
151659cc4ca5SDavid du Colombier return hardreject;
15177dd7cddfSDavid du Colombier }
15189a747e4fSDavid du Colombier
15199a747e4fSDavid du Colombier /*
15209a747e4fSDavid du Colombier * create abs path of the mailer
15219a747e4fSDavid du Colombier */
15229a747e4fSDavid du Colombier String*
mailerpath(char * p)15239a747e4fSDavid du Colombier mailerpath(char *p)
15249a747e4fSDavid du Colombier {
15259a747e4fSDavid du Colombier String *s;
15269a747e4fSDavid du Colombier
15279a747e4fSDavid du Colombier if(p == nil)
15289a747e4fSDavid du Colombier return nil;
15299a747e4fSDavid du Colombier if(*p == '/')
15309a747e4fSDavid du Colombier return s_copy(p);
15319a747e4fSDavid du Colombier s = s_new();
15329a747e4fSDavid du Colombier s_append(s, UPASBIN);
15339a747e4fSDavid du Colombier s_append(s, "/");
15349a747e4fSDavid du Colombier s_append(s, p);
15359a747e4fSDavid du Colombier return s;
15369a747e4fSDavid du Colombier }
15373ff48bf5SDavid du Colombier
15383ff48bf5SDavid du Colombier String *
s_dec64(String * sin)15393ff48bf5SDavid du Colombier s_dec64(String *sin)
15403ff48bf5SDavid du Colombier {
15413ff48bf5SDavid du Colombier int lin, lout;
15420c5b33aeSDavid du Colombier String *sout;
15430c5b33aeSDavid du Colombier
15440827824dSDavid du Colombier lin = s_len(sin);
15450827824dSDavid du Colombier
15460827824dSDavid du Colombier /*
15470827824dSDavid du Colombier * if the string is coming from smtpd.y, it will have no nl.
15480827824dSDavid du Colombier * if it is coming from getcrnl below, it will have an nl.
15490827824dSDavid du Colombier */
15500827824dSDavid du Colombier if (*(s_to_c(sin)+lin-1) == '\n')
15510827824dSDavid du Colombier lin--;
15520827824dSDavid du Colombier sout = s_newalloc(lin+1);
15533ff48bf5SDavid du Colombier lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin);
15543ff48bf5SDavid du Colombier if (lout < 0) {
15553ff48bf5SDavid du Colombier s_free(sout);
15563ff48bf5SDavid du Colombier return nil;
15573ff48bf5SDavid du Colombier }
15583ff48bf5SDavid du Colombier sout->ptr = sout->base + lout;
15593ff48bf5SDavid du Colombier s_terminate(sout);
15603ff48bf5SDavid du Colombier return sout;
15613ff48bf5SDavid du Colombier }
15623ff48bf5SDavid du Colombier
15633ff48bf5SDavid du Colombier void
starttls(void)1564dddc47c2SDavid du Colombier starttls(void)
1565dddc47c2SDavid du Colombier {
1566dddc47c2SDavid du Colombier int certlen, fd;
15670c5b33aeSDavid du Colombier uchar *cert;
1568dddc47c2SDavid du Colombier TLSconn *conn;
1569dddc47c2SDavid du Colombier
1570314a20f0SDavid du Colombier if (tlscert == nil) {
15710c5b33aeSDavid du Colombier reply("500 5.5.1 illegal command or bad syntax\r\n");
1572314a20f0SDavid du Colombier return;
1573314a20f0SDavid du Colombier }
1574dddc47c2SDavid du Colombier conn = mallocz(sizeof *conn, 1);
1575679c15e8SDavid du Colombier cert = readcert(tlscert, &certlen);
1576dddc47c2SDavid du Colombier if (conn == nil || cert == nil) {
1577dddc47c2SDavid du Colombier if (conn != nil)
1578dddc47c2SDavid du Colombier free(conn);
15790c5b33aeSDavid du Colombier reply("454 4.7.5 TLS not available\r\n");
1580dddc47c2SDavid du Colombier return;
1581dddc47c2SDavid du Colombier }
15820c5b33aeSDavid du Colombier reply("220 2.0.0 Go ahead make my day\r\n");
1583dddc47c2SDavid du Colombier conn->cert = cert;
1584dddc47c2SDavid du Colombier conn->certlen = certlen;
1585dddc47c2SDavid du Colombier fd = tlsServer(Bfildes(&bin), conn);
1586dddc47c2SDavid du Colombier if (fd < 0) {
1587dddc47c2SDavid du Colombier free(cert);
1588dddc47c2SDavid du Colombier free(conn);
1589a50688edSDavid du Colombier syslog(0, "smtpd", "TLS start-up failed with %s", him);
1590a50688edSDavid du Colombier
1591a50688edSDavid du Colombier /* force the client to hang up */
1592a50688edSDavid du Colombier close(Bfildes(&bin)); /* probably fd 0 */
1593a50688edSDavid du Colombier close(1);
1594a50688edSDavid du Colombier exits("tls failed");
1595dddc47c2SDavid du Colombier }
1596dddc47c2SDavid du Colombier Bterm(&bin);
1597dddc47c2SDavid du Colombier Binit(&bin, fd, OREAD);
1598fe096d1aSDavid du Colombier if (dup(fd, 1) < 0)
1599fe096d1aSDavid du Colombier fprint(2, "dup of %d failed: %r\n", fd);
1600dddc47c2SDavid du Colombier passwordinclear = 1;
1601fe096d1aSDavid du Colombier syslog(0, "smtpd", "started TLS with %s", him);
1602dddc47c2SDavid du Colombier }
1603dddc47c2SDavid du Colombier
1604dddc47c2SDavid du Colombier void
auth(String * mech,String * resp)1605d9306527SDavid du Colombier auth(String *mech, String *resp)
16063ff48bf5SDavid du Colombier {
16070c5b33aeSDavid du Colombier char *user, *pass, *scratch = nil;
16083ff48bf5SDavid du Colombier AuthInfo *ai = nil;
16090c5b33aeSDavid du Colombier Chalstate *chs = nil;
16100c5b33aeSDavid du Colombier String *s_resp1_64 = nil, *s_resp2_64 = nil, *s_resp1 = nil;
16113ff48bf5SDavid du Colombier String *s_resp2 = nil;
16123ff48bf5SDavid du Colombier
16133ff48bf5SDavid du Colombier if (rejectcheck())
16143ff48bf5SDavid du Colombier goto bomb_out;
16153ff48bf5SDavid du Colombier
16162b7fd5adSDavid du Colombier syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
1617119a69faSDavid du Colombier "(protected)", him);
16180827824dSDavid du Colombier
16193ff48bf5SDavid du Colombier if (authenticated) {
16203ff48bf5SDavid du Colombier bad_sequence:
16213ff48bf5SDavid du Colombier rejectcount++;
16220c5b33aeSDavid du Colombier reply("503 5.5.2 Bad sequence of commands\r\n");
16233ff48bf5SDavid du Colombier goto bomb_out;
16243ff48bf5SDavid du Colombier }
1625d9306527SDavid du Colombier if (cistrcmp(s_to_c(mech), "plain") == 0) {
16263ff48bf5SDavid du Colombier if (!passwordinclear) {
16273ff48bf5SDavid du Colombier rejectcount++;
16280c5b33aeSDavid du Colombier reply("538 5.7.1 Encryption required for requested "
16290c5b33aeSDavid du Colombier "authentication mechanism\r\n");
16303ff48bf5SDavid du Colombier goto bomb_out;
16313ff48bf5SDavid du Colombier }
1632d9306527SDavid du Colombier s_resp1_64 = resp;
1633d9306527SDavid du Colombier if (s_resp1_64 == nil) {
1634d9306527SDavid du Colombier reply("334 \r\n");
1635d9306527SDavid du Colombier s_resp1_64 = s_new();
16360c5b33aeSDavid du Colombier if (getcrnl(s_resp1_64, &bin) <= 0)
1637d9306527SDavid du Colombier goto bad_sequence;
1638d9306527SDavid du Colombier }
1639d9306527SDavid du Colombier s_resp1 = s_dec64(s_resp1_64);
1640d9306527SDavid du Colombier if (s_resp1 == nil) {
1641d9306527SDavid du Colombier rejectcount++;
16420c5b33aeSDavid du Colombier reply("501 5.5.4 Cannot decode base64\r\n");
1643d9306527SDavid du Colombier goto bomb_out;
1644d9306527SDavid du Colombier }
1645d9306527SDavid du Colombier memset(s_to_c(s_resp1_64), 'X', s_len(s_resp1_64));
1646e541223cSDavid du Colombier user = s_to_c(s_resp1) + strlen(s_to_c(s_resp1)) + 1;
1647e541223cSDavid du Colombier pass = user + strlen(user) + 1;
1648d9306527SDavid du Colombier ai = auth_userpasswd(user, pass);
1649d9306527SDavid du Colombier authenticated = ai != nil;
1650d9306527SDavid du Colombier memset(pass, 'X', strlen(pass));
1651d9306527SDavid du Colombier goto windup;
1652d9306527SDavid du Colombier }
1653d9306527SDavid du Colombier else if (cistrcmp(s_to_c(mech), "login") == 0) {
1654d9306527SDavid du Colombier if (!passwordinclear) {
1655d9306527SDavid du Colombier rejectcount++;
16560c5b33aeSDavid du Colombier reply("538 5.7.1 Encryption required for requested "
16570c5b33aeSDavid du Colombier "authentication mechanism\r\n");
1658d9306527SDavid du Colombier goto bomb_out;
1659d9306527SDavid du Colombier }
1660d9306527SDavid du Colombier if (resp == nil) {
16613ff48bf5SDavid du Colombier reply("334 VXNlcm5hbWU6\r\n");
16623ff48bf5SDavid du Colombier s_resp1_64 = s_new();
16633ff48bf5SDavid du Colombier if (getcrnl(s_resp1_64, &bin) <= 0)
16643ff48bf5SDavid du Colombier goto bad_sequence;
1665d9306527SDavid du Colombier }
16663ff48bf5SDavid du Colombier reply("334 UGFzc3dvcmQ6\r\n");
16673ff48bf5SDavid du Colombier s_resp2_64 = s_new();
16683ff48bf5SDavid du Colombier if (getcrnl(s_resp2_64, &bin) <= 0)
16693ff48bf5SDavid du Colombier goto bad_sequence;
16703ff48bf5SDavid du Colombier s_resp1 = s_dec64(s_resp1_64);
16713ff48bf5SDavid du Colombier s_resp2 = s_dec64(s_resp2_64);
16723ff48bf5SDavid du Colombier memset(s_to_c(s_resp2_64), 'X', s_len(s_resp2_64));
16733ff48bf5SDavid du Colombier if (s_resp1 == nil || s_resp2 == nil) {
16743ff48bf5SDavid du Colombier rejectcount++;
16750c5b33aeSDavid du Colombier reply("501 5.5.4 Cannot decode base64\r\n");
16763ff48bf5SDavid du Colombier goto bomb_out;
16773ff48bf5SDavid du Colombier }
16783ff48bf5SDavid du Colombier ai = auth_userpasswd(s_to_c(s_resp1), s_to_c(s_resp2));
16793ff48bf5SDavid du Colombier authenticated = ai != nil;
16803ff48bf5SDavid du Colombier memset(s_to_c(s_resp2), 'X', s_len(s_resp2));
16813ff48bf5SDavid du Colombier windup:
1682db328f6cSDavid du Colombier if (authenticated) {
1683db328f6cSDavid du Colombier /* if you authenticated, we trust you despite your IP */
1684db328f6cSDavid du Colombier trusted = 1;
16850c5b33aeSDavid du Colombier reply("235 2.0.0 Authentication successful\r\n");
1686db328f6cSDavid du Colombier } else {
16873ff48bf5SDavid du Colombier rejectcount++;
16880c5b33aeSDavid du Colombier reply("535 5.7.1 Authentication failed\r\n");
1689617c0e1eSDavid du Colombier syslog(0, "smtpd", "authentication failed: %r");
16903ff48bf5SDavid du Colombier }
16913ff48bf5SDavid du Colombier goto bomb_out;
16923ff48bf5SDavid du Colombier }
16933ff48bf5SDavid du Colombier else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
16943ff48bf5SDavid du Colombier int chal64n;
16950c5b33aeSDavid du Colombier char *resp, *t;
16963ff48bf5SDavid du Colombier
16973ff48bf5SDavid du Colombier chs = auth_challenge("proto=cram role=server");
16983ff48bf5SDavid du Colombier if (chs == nil) {
16993ff48bf5SDavid du Colombier rejectcount++;
17000c5b33aeSDavid du Colombier reply("501 5.7.5 Couldn't get CRAM-MD5 challenge\r\n");
17013ff48bf5SDavid du Colombier goto bomb_out;
17023ff48bf5SDavid du Colombier }
17033ff48bf5SDavid du Colombier scratch = malloc(chs->nchal * 2 + 1);
17040c5b33aeSDavid du Colombier chal64n = enc64(scratch, chs->nchal * 2, (uchar *)chs->chal,
17050c5b33aeSDavid du Colombier chs->nchal);
17063ff48bf5SDavid du Colombier scratch[chal64n] = 0;
17073ff48bf5SDavid du Colombier reply("334 %s\r\n", scratch);
17083ff48bf5SDavid du Colombier s_resp1_64 = s_new();
17093ff48bf5SDavid du Colombier if (getcrnl(s_resp1_64, &bin) <= 0)
17103ff48bf5SDavid du Colombier goto bad_sequence;
17113ff48bf5SDavid du Colombier s_resp1 = s_dec64(s_resp1_64);
17123ff48bf5SDavid du Colombier if (s_resp1 == nil) {
17133ff48bf5SDavid du Colombier rejectcount++;
17140c5b33aeSDavid du Colombier reply("501 5.5.4 Cannot decode base64\r\n");
17153ff48bf5SDavid du Colombier goto bomb_out;
17163ff48bf5SDavid du Colombier }
17170827824dSDavid du Colombier /* should be of form <user><space><response> */
17183ff48bf5SDavid du Colombier resp = s_to_c(s_resp1);
17193ff48bf5SDavid du Colombier t = strchr(resp, ' ');
17203ff48bf5SDavid du Colombier if (t == nil) {
17213ff48bf5SDavid du Colombier rejectcount++;
17220c5b33aeSDavid du Colombier reply("501 5.5.4 Poorly formed CRAM-MD5 response\r\n");
17233ff48bf5SDavid du Colombier goto bomb_out;
17243ff48bf5SDavid du Colombier }
17253ff48bf5SDavid du Colombier *t++ = 0;
17263ff48bf5SDavid du Colombier chs->user = resp;
17273ff48bf5SDavid du Colombier chs->resp = t;
17283ff48bf5SDavid du Colombier chs->nresp = strlen(t);
17293ff48bf5SDavid du Colombier ai = auth_response(chs);
17303ff48bf5SDavid du Colombier authenticated = ai != nil;
17313ff48bf5SDavid du Colombier goto windup;
17323ff48bf5SDavid du Colombier }
17333ff48bf5SDavid du Colombier rejectcount++;
17340c5b33aeSDavid du Colombier reply("501 5.5.1 Unrecognised authentication type %s\r\n", s_to_c(mech));
17353ff48bf5SDavid du Colombier bomb_out:
17363ff48bf5SDavid du Colombier if (ai)
17373ff48bf5SDavid du Colombier auth_freeAI(ai);
17383ff48bf5SDavid du Colombier if (chs)
17393ff48bf5SDavid du Colombier auth_freechal(chs);
17403ff48bf5SDavid du Colombier if (scratch)
17413ff48bf5SDavid du Colombier free(scratch);
17423ff48bf5SDavid du Colombier if (s_resp1)
17433ff48bf5SDavid du Colombier s_free(s_resp1);
17443ff48bf5SDavid du Colombier if (s_resp2)
17453ff48bf5SDavid du Colombier s_free(s_resp2);
17463ff48bf5SDavid du Colombier if (s_resp1_64)
17473ff48bf5SDavid du Colombier s_free(s_resp1_64);
17483ff48bf5SDavid du Colombier if (s_resp2_64)
17493ff48bf5SDavid du Colombier s_free(s_resp2_64);
17503ff48bf5SDavid du Colombier }
1751*c1dd2601SDavid du Colombier
1752