xref: /plan9/sys/src/cmd/upas/smtp/smtpd.c (revision c1dd2601e42f5138f07719cdf2819a51c0f6f850)
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