xref: /plan9/sys/src/cmd/upas/smtp/smtpd.c (revision c1dd2601e42f5138f07719cdf2819a51c0f6f850)
1 #include "common.h"
2 #include "smtpd.h"
3 #include "smtp.h"
4 #include <ctype.h>
5 #include <ip.h>
6 #include <ndb.h>
7 #include <mp.h>
8 #include <libsec.h>
9 #include <auth.h>
10 #include "../smtp/y.tab.h"
11 
12 char	*me;
13 char	*him="";
14 char	*dom;
15 process	*pp;
16 String	*mailer;
17 NetConnInfo *nci;
18 
19 int	filterstate = ACCEPT;
20 int	trusted;
21 int	logged;
22 int	rejectcount;
23 int	hardreject;
24 
25 ulong	starttime;
26 
27 Biobuf	bin;
28 
29 int	debug;
30 int	Dflag;
31 int	fflag;
32 int	gflag;
33 int	rflag;
34 int	sflag;
35 int	authenticate;
36 int	authenticated;
37 int	passwordinclear;
38 char	*tlscert;
39 
40 uchar	rsysip[IPaddrlen];
41 
42 List	senders;
43 List	rcvers;
44 
45 char	pipbuf[ERRMAX];
46 char	*piperror;
47 
48 String*	mailerpath(char*);
49 int	pipemsg(int*);
50 int	rejectcheck(void);
51 String*	startcmd(void);
52 
53 static void	logmsg(char *action);
54 static int	delaysecs(void);
55 
56 static int
catchalarm(void * a,char * msg)57 catchalarm(void *a, char *msg)
58 {
59 	int rv;
60 
61 	USED(a);
62 
63 	/* log alarms but continue */
64 	if(strstr(msg, "alarm") != nil){
65 		if(senders.first && senders.first->p &&
66 		    rcvers.first && rcvers.first->p)
67 			syslog(0, "smtpd", "note: %s->%s: %s",
68 				s_to_c(senders.first->p),
69 				s_to_c(rcvers.first->p), msg);
70 		else
71 			syslog(0, "smtpd", "note: %s", msg);
72 		rv = Atnoterecog;
73 	} else
74 		rv = Atnoteunknown;
75 	if (debug) {
76 		seek(2, 0, 2);
77 		fprint(2, "caught note: %s\n", msg);
78 	}
79 
80 	/* kill the children if there are any */
81 	if(pp && pp->pid > 0) {
82 		syskillpg(pp->pid);
83 		/* none can't syskillpg, so try a variant */
84 		sleep(500);
85 		syskill(pp->pid);
86 	}
87 
88 	return rv;
89 }
90 
91 /* override string error functions to do something reasonable */
92 void
s_error(char * f,char * status)93 s_error(char *f, char *status)
94 {
95 	char errbuf[Errlen];
96 
97 	errbuf[0] = 0;
98 	rerrstr(errbuf, sizeof(errbuf));
99 	if(f && *f)
100 		reply("452 4.3.0 out of memory %s: %s\r\n", f, errbuf);
101 	else
102 		reply("452 4.3.0 out of memory %s\r\n", errbuf);
103 	syslog(0, "smtpd", "++Malloc failure %s [%s]", him, nci->rsys);
104 	exits(status);
105 }
106 
107 static void
usage(void)108 usage(void)
109 {
110 	fprint(2,
111 	  "usage: smtpd [-adDfghprs] [-c cert] [-k ip] [-m mailer] [-n net]\n");
112 	exits("usage");
113 }
114 
115 void
main(int argc,char ** argv)116 main(int argc, char **argv)
117 {
118 	char *netdir;
119 	char buf[1024];
120 
121 	netdir = nil;
122 	quotefmtinstall();
123 	fmtinstall('I', eipfmt);
124 	starttime = time(0);
125 	ARGBEGIN{
126 	case 'a':
127 		authenticate = 1;
128 		break;
129 	case 'c':
130 		tlscert = EARGF(usage());
131 		break;
132 	case 'D':
133 		Dflag++;
134 		break;
135 	case 'd':
136 		debug++;
137 		break;
138 	case 'f':				/* disallow relaying */
139 		fflag = 1;
140 		break;
141 	case 'g':
142 		gflag = 1;
143 		break;
144 	case 'h':				/* default domain name */
145 		dom = EARGF(usage());
146 		break;
147 	case 'k':				/* prohibited ip address */
148 		addbadguy(EARGF(usage()));
149 		break;
150 	case 'm':				/* set mail command */
151 		mailer = mailerpath(EARGF(usage()));
152 		break;
153 	case 'n':				/* log peer ip address */
154 		netdir = EARGF(usage());
155 		break;
156 	case 'p':
157 		passwordinclear = 1;
158 		break;
159 	case 'r':
160 		rflag = 1;			/* verify sender's domain */
161 		break;
162 	case 's':				/* save blocked messages */
163 		sflag = 1;
164 		break;
165 	case 't':
166 		fprint(2, "%s: the -t option is no longer supported, see -c\n",
167 			argv0);
168 		tlscert = "/sys/lib/ssl/smtpd-cert.pem";
169 		break;
170 	default:
171 		usage();
172 	}ARGEND;
173 
174 	nci = getnetconninfo(netdir, 0);
175 	if(nci == nil)
176 		sysfatal("can't get remote system's address: %r");
177 	parseip(rsysip, nci->rsys);
178 
179 	if(mailer == nil)
180 		mailer = mailerpath("send");
181 
182 	if(debug){
183 		snprint(buf, sizeof buf, "%s/smtpdb/%ld", UPASLOG, time(0));
184 		close(2);
185 		if (create(buf, OWRITE | OEXCL, 0662) >= 0) {
186 			seek(2, 0, 2);
187 			fprint(2, "%d smtpd %s\n", getpid(), thedate());
188 		} else
189 			debug = 0;
190 	}
191 	getconf();
192 	if (isbadguy())
193 		exits("banned");
194 	Binit(&bin, 0, OREAD);
195 
196 	if (chdir(UPASLOG) < 0)
197 		syslog(0, "smtpd", "no %s: %r", UPASLOG);
198 	me = sysname_read();
199 	if(dom == 0 || dom[0] == 0)
200 		dom = domainname_read();
201 	if(dom == 0 || dom[0] == 0)
202 		dom = me;
203 	parseinit();
204 	sayhi();
205 
206 	/* allow 45 minutes to parse the header */
207 	atnotify(catchalarm, 1);
208 	alarm(45*60*1000);
209 	zzparse();
210 	exits(0);
211 }
212 
213 void
listfree(List * l)214 listfree(List *l)
215 {
216 	Link *lp, *next;
217 
218 	for(lp = l->first; lp; lp = next){
219 		next = lp->next;
220 		s_free(lp->p);
221 		free(lp);
222 	}
223 	l->first = l->last = 0;
224 }
225 
226 void
listadd(List * l,String * path)227 listadd(List *l, String *path)
228 {
229 	Link *lp;
230 
231 	lp = (Link *)malloc(sizeof *lp);
232 	lp->p = path;
233 	lp->next = 0;
234 
235 	if(l->last)
236 		l->last->next = lp;
237 	else
238 		l->first = lp;
239 	l->last = lp;
240 }
241 
242 void
stamp(void)243 stamp(void)
244 {
245 	if(debug) {
246 		seek(2, 0, 2);
247 		fprint(2, "%3lud ", time(0) - starttime);
248 	}
249 }
250 
251 #define	SIZE	4096
252 
253 int
reply(char * fmt,...)254 reply(char *fmt, ...)
255 {
256 	long n;
257 	char buf[SIZE], *out;
258 	va_list arg;
259 
260 	va_start(arg, fmt);
261 	out = vseprint(buf, buf+SIZE, fmt, arg);
262 	va_end(arg);
263 
264 	n = out - buf;
265 	if(debug) {
266 		seek(2, 0, 2);
267 		stamp();
268 		write(2, buf, n);
269 	}
270 	write(1, buf, n);
271 	return n;
272 }
273 
274 void
reset(void)275 reset(void)
276 {
277 	if(rejectcheck())
278 		return;
279 	listfree(&rcvers);
280 	listfree(&senders);
281 	if(filterstate != DIALUP){
282 		logged = 0;
283 		filterstate = ACCEPT;
284 	}
285 	reply("250 2.0.0 ok\r\n");
286 }
287 
288 void
sayhi(void)289 sayhi(void)
290 {
291 	Dir *dp;
292 
293 	reply("220-%s ESMTP\r\n", dom);
294 	sleep(3000);
295 	dp = dirfstat(0);
296 	if (dp && dp->length > 0) {
297 		syslog(0, "smtpd", "Hung up on impatient spammer %s", nci->rsys);
298 		if(Dflag)
299 			sleep(delaysecs()*1000);
300 		reply("554 5.7.0 Spammer!\r\n");
301 		exits("spammer didn't wait for greeting to finish");
302 	}
303 	free(dp);
304 	reply("220 \r\n");
305 }
306 
307 /*
308  * make callers from class A networks infested by spammers
309  * wait longer.
310  */
311 
312 static char netaspam[256] = {
313 	[58]	1,
314 	[66]	1,
315 	[71]	1,
316 
317 	[76]	1,
318 	[77]	1,
319 	[78]	1,
320 	[79]	1,
321 	[80]	1,
322 	[81]	1,
323 	[82]	1,
324 	[83]	1,
325 	[84]	1,
326 	[85]	1,
327 	[86]	1,
328 	[87]	1,
329 	[88]	1,
330 	[89]	1,
331 
332 	[190]	1,
333 	[201]	1,
334 	[217]	1,
335 };
336 
337 static int
delaysecs(void)338 delaysecs(void)
339 {
340 	if (trusted)
341 		return 0;
342 	if (0 && netaspam[rsysip[0]])
343 		return 20;
344 	return 12;
345 }
346 
347 void
hello(String * himp,int extended)348 hello(String *himp, int extended)
349 {
350 	char **mynames;
351 	char *ldot, *rdot;
352 
353 	him = s_to_c(himp);
354 	syslog(0, "smtpd", "%s from %s as %s", extended? "ehlo": "helo",
355 		nci->rsys, him);
356 	if(rejectcheck())
357 		return;
358 
359 	if (strchr(him, '.') && nci && !trusted && fflag &&
360 	    strcmp(nci->rsys, nci->lsys) != 0){
361 		/*
362 		 * We don't care if he lies about who he is, but it is
363 		 * not okay to pretend to be us.  Many viruses do this,
364 		 * just parroting back what we say in the greeting.
365 		 */
366 		if(strcmp(him, dom) == 0)
367 			goto Liarliar;
368 		for(mynames = sysnames_read(); mynames && *mynames; mynames++){
369 			if(cistrcmp(*mynames, him) == 0){
370 Liarliar:
371 				syslog(0, "smtpd",
372 					"Hung up on %s; claimed to be %s",
373 					nci->rsys, him);
374 				if(Dflag)
375 					sleep(delaysecs()*1000);
376 				reply("554 5.7.0 Liar!\r\n");
377 				exits("client pretended to be us");
378 				return;
379 			}
380 		}
381 	}
382 
383 	/*
384 	 * it is unacceptable to claim any string that doesn't look like
385 	 * a domain name (e.g., has at least one dot in it), but
386 	 * Microsoft mail client software gets this wrong, so let trusted
387 	 * (local) clients omit the dot.
388 	 */
389 	rdot = strrchr(him, '.');
390 	if (rdot && rdot[1] == '\0') {
391 		*rdot = '\0';			/* clobber trailing dot */
392 		rdot = strrchr(him, '.');	/* try again */
393 	}
394 	if (!trusted && rdot == nil)
395 		goto Liarliar;
396 	/*
397 	 * Reject obviously bogus domains and those reserved by RFC 2606.
398 	 */
399 	if (rdot == nil)
400 		rdot = him;
401 	else
402 		rdot++;
403 	if (!trusted && (cistrcmp(rdot, "localdomain") == 0 ||
404 	    cistrcmp(rdot, "localhost") == 0 ||
405 	    cistrcmp(rdot, "example") == 0 ||
406 	    cistrcmp(rdot, "invalid") == 0 ||
407 	    cistrcmp(rdot, "test") == 0))
408 		goto Liarliar;			/* bad top-level domain */
409 	/* check second-level RFC 2606 domains: example\.(com|net|org) */
410 	if (rdot != him)
411 		*--rdot = '\0';
412 	ldot = strrchr(him, '.');
413 	if (rdot != him)
414 		*rdot = '.';
415 	if (ldot == nil)
416 		ldot = him;
417 	else
418 		ldot++;
419 	if (cistrcmp(ldot, "example.com") == 0 ||
420 	    cistrcmp(ldot, "example.net") == 0 ||
421 	    cistrcmp(ldot, "example.org") == 0)
422 		goto Liarliar;
423 
424 	/*
425 	 * similarly, if the claimed domain is not an address-literal,
426 	 * require at least one letter, which there will be in
427 	 * at least the last component (e.g., .com, .net) if it's real.
428 	 * this rejects non-address-literal IP addresses,
429 	 * among other bogosities.
430 	 */
431 	if (!trusted && him[0] != '[') {
432 		char *p;
433 
434 		for (p = him; *p != '\0'; p++)
435 			if (isascii(*p) && isalpha(*p))
436 				break;
437 		if (*p == '\0')
438 			goto Liarliar;
439 	}
440 	if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil)
441 		him = nci->rsys;
442 
443 	if(Dflag)
444 		sleep(delaysecs()*1000);
445 	reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him);
446 	if (extended) {
447 		reply("250-ENHANCEDSTATUSCODES\r\n");	/* RFCs 2034 and 3463 */
448 		if(tlscert != nil)
449 			reply("250-STARTTLS\r\n");
450 		if (passwordinclear)
451 			reply("250 AUTH CRAM-MD5 PLAIN LOGIN\r\n");
452 		else
453 			reply("250 AUTH CRAM-MD5\r\n");
454 	}
455 }
456 
457 void
sender(String * path)458 sender(String *path)
459 {
460 	String *s;
461 	static char *lastsender;
462 
463 	if(rejectcheck())
464 		return;
465 	if (authenticate && !authenticated) {
466 		rejectcount++;
467 		reply("530 5.7.0 Authentication required\r\n");
468 		return;
469 	}
470 	if(him == 0 || *him == 0){
471 		rejectcount++;
472 		reply("503 Start by saying HELO, please.\r\n", s_to_c(path));
473 		return;
474 	}
475 
476 	/* don't add the domain onto black holes or we will loop */
477 	if(strchr(s_to_c(path), '!') == 0 && strcmp(s_to_c(path), "/dev/null") != 0){
478 		s = s_new();
479 		s_append(s, him);
480 		s_append(s, "!");
481 		s_append(s, s_to_c(path));
482 		s_terminate(s);
483 		s_free(path);
484 		path = s;
485 	}
486 	if(shellchars(s_to_c(path))){
487 		rejectcount++;
488 		reply("501 5.1.3 Bad character in sender address %s.\r\n",
489 			s_to_c(path));
490 		return;
491 	}
492 
493 	/*
494 	 * if the last sender address resulted in a rejection because the sending
495 	 * domain didn't exist and this sender has the same domain, reject
496 	 * immediately.
497 	 */
498 	if(lastsender){
499 		if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){
500 			filterstate = REFUSED;
501 			rejectcount++;
502 			reply("554 5.1.8 Sender domain must exist: %s\r\n",
503 				s_to_c(path));
504 			return;
505 		}
506 		free(lastsender);	/* different sender domain */
507 		lastsender = 0;
508 	}
509 
510 	/*
511 	 * see if this ip address, domain name, user name or account is blocked
512 	 */
513 	logged = 0;
514 	filterstate = blocked(path);
515 	/*
516 	 * permanently reject what we can before trying smtp ping, which
517 	 * often leads to merely temporary rejections.
518 	 */
519 	switch (filterstate){
520 	case DENIED:
521 		syslog(0, "smtpd", "Denied %s (%s/%s)",
522 			s_to_c(path), him, nci->rsys);
523 		rejectcount++;
524 		logged++;
525 		reply("554-5.7.1 We don't accept mail from %s.\r\n",
526 			s_to_c(path));
527 		reply("554 5.7.1 Contact postmaster@%s for more information.\r\n",
528 			dom);
529 		return;
530 	case REFUSED:
531 		syslog(0, "smtpd", "Refused %s (%s/%s)",
532 			s_to_c(path), him, nci->rsys);
533 		rejectcount++;
534 		logged++;
535 		reply("554 5.7.1 Sender domain must exist: %s\r\n",
536 			s_to_c(path));
537 		return;
538 	}
539 
540 	listadd(&senders, path);
541 	reply("250 2.0.0 sender is %s\r\n", s_to_c(path));
542 }
543 
544 enum { Rcpt, Domain, Ntoks };
545 
546 typedef struct Sender Sender;
547 struct Sender {
548 	Sender	*next;
549 	char	*rcpt;
550 	char	*domain;
551 };
552 static Sender *sendlist, *sendlast;
553 
554 static int
rdsenders(void)555 rdsenders(void)
556 {
557 	int lnlen, nf, ok = 1;
558 	char *line, *senderfile;
559 	char *toks[Ntoks];
560 	Biobuf *sf;
561 	Sender *snd;
562 	static int beenhere = 0;
563 
564 	if (beenhere)
565 		return 1;
566 	beenhere = 1;
567 
568 	/*
569 	 * we're sticking with a system-wide sender list because
570 	 * per-user lists would require fully resolving recipient
571 	 * addresses to determine which users they correspond to
572 	 * (barring exploiting syntactic conventions).
573 	 */
574 	senderfile = smprint("%s/senders", UPASLIB);
575 	sf = Bopen(senderfile, OREAD);
576 	free(senderfile);
577 	if (sf == nil)
578 		return 1;
579 	while ((line = Brdline(sf, '\n')) != nil) {
580 		if (line[0] == '#' || line[0] == '\n')
581 			continue;
582 		lnlen = Blinelen(sf);
583 		line[lnlen-1] = '\0';		/* clobber newline */
584 		nf = tokenize(line, toks, nelem(toks));
585 		if (nf != nelem(toks))
586 			continue;		/* malformed line */
587 
588 		snd = malloc(sizeof *snd);
589 		if (snd == nil)
590 			sysfatal("out of memory: %r");
591 		memset(snd, 0, sizeof *snd);
592 		snd->next = nil;
593 
594 		if (sendlast == nil)
595 			sendlist = snd;
596 		else
597 			sendlast->next = snd;
598 		sendlast = snd;
599 		snd->rcpt = strdup(toks[Rcpt]);
600 		snd->domain = strdup(toks[Domain]);
601 	}
602 	Bterm(sf);
603 	return ok;
604 }
605 
606 /*
607  * read (recipient, sender's DNS) pairs from /mail/lib/senders.
608  * Only allow mail to recipient from any of sender's IPs.
609  * A recipient not mentioned in the file is always permitted.
610  */
611 static int
senderok(char * rcpt)612 senderok(char *rcpt)
613 {
614 	int mentioned = 0, matched = 0;
615 	uchar dnsip[IPaddrlen];
616 	Sender *snd;
617 	Ndbtuple *nt, *next, *first;
618 
619 	rdsenders();
620 	for (snd = sendlist; snd != nil; snd = snd->next) {
621 		if (strcmp(rcpt, snd->rcpt) != 0)
622 			continue;
623 		/*
624 		 * see if this domain's ips match nci->rsys.
625 		 * if not, perhaps a later entry's domain will.
626 		 */
627 		mentioned = 1;
628 		if (parseip(dnsip, snd->domain) != -1 &&
629 		    memcmp(rsysip, dnsip, IPaddrlen) == 0)
630 			return 1;
631 		/*
632 		 * NB: nt->line links form a circular list(!).
633 		 * we need to make one complete pass over it to free it all.
634 		 */
635 		first = nt = dnsquery(nci->root, snd->domain, "ip");
636 		if (first == nil)
637 			continue;
638 		do {
639 			if (strcmp(nt->attr, "ip") == 0 &&
640 			    parseip(dnsip, nt->val) != -1 &&
641 			    memcmp(rsysip, dnsip, IPaddrlen) == 0)
642 				matched = 1;
643 			next = nt->line;
644 			free(nt);
645 			nt = next;
646 		} while (nt != first);
647 	}
648 	if (matched)
649 		return 1;
650 	else
651 		return !mentioned;
652 }
653 
654 void
receiver(String * path)655 receiver(String *path)
656 {
657 	char *sender, *rcpt;
658 
659 	if(rejectcheck())
660 		return;
661 	if(him == 0 || *him == 0){
662 		rejectcount++;
663 		reply("503 Start by saying HELO, please\r\n");
664 		return;
665 	}
666 	if(senders.last)
667 		sender = s_to_c(senders.last->p);
668 	else
669 		sender = "<unknown>";
670 
671 	if(!recipok(s_to_c(path))){
672 		rejectcount++;
673 		syslog(0, "smtpd",
674 		 "Disallowed %s (%s/%s) to blocked, unknown or invalid name %s",
675 			sender, him, nci->rsys, s_to_c(path));
676 		reply("550 5.1.1 %s ... user unknown\r\n", s_to_c(path));
677 		return;
678 	}
679 	rcpt = s_to_c(path);
680 	if (!senderok(rcpt)) {
681 		rejectcount++;
682 		syslog(0, "smtpd", "Disallowed sending IP of %s (%s/%s) to %s",
683 			sender, him, nci->rsys, rcpt);
684 		reply("550 5.7.1 %s ... sending system not allowed\r\n", rcpt);
685 		return;
686 	}
687 
688 	logged = 0;
689 
690 	/* forwarding() can modify 'path' on loopback request */
691 	if(filterstate == ACCEPT && fflag && !authenticated && forwarding(path)) {
692 		syslog(0, "smtpd", "Bad Forward %s (%s/%s) (%s)",
693 			senders.last && senders.last->p?
694 				s_to_c(senders.last->p): sender,
695 			him, nci->rsys, path? s_to_c(path): rcpt);
696 		rejectcount++;
697 		reply("550 5.7.1 we don't relay.  send to your-path@[] for "
698 			"loopback.\r\n");
699 		return;
700 	}
701 	listadd(&rcvers, path);
702 	reply("250 2.0.0 receiver is %s\r\n", s_to_c(path));
703 }
704 
705 void
quit(void)706 quit(void)
707 {
708 	reply("221 2.0.0 Successful termination\r\n");
709 	if(debug){
710 		seek(2, 0, 2);
711 		stamp();
712 		fprint(2, "# %d sent 221 reply to QUIT %s\n",
713 			getpid(), thedate());
714 	}
715 	close(0);
716 	exits(0);
717 }
718 
719 void
noop(void)720 noop(void)
721 {
722 	if(rejectcheck())
723 		return;
724 	reply("250 2.0.0 Nothing to see here. Move along ...\r\n");
725 }
726 
727 void
help(String * cmd)728 help(String *cmd)
729 {
730 	if(rejectcheck())
731 		return;
732 	if(cmd)
733 		s_free(cmd);
734 	reply("250 2.0.0 See http://www.ietf.org/rfc/rfc2821\r\n");
735 }
736 
737 void
verify(String * path)738 verify(String *path)
739 {
740 	char *p, *q;
741 	char *av[4];
742 
743 	if(rejectcheck())
744 		return;
745 	if(shellchars(s_to_c(path))){
746 		reply("503 5.1.3 Bad character in address %s.\r\n", s_to_c(path));
747 		return;
748 	}
749 	av[0] = s_to_c(mailer);
750 	av[1] = "-x";
751 	av[2] = s_to_c(path);
752 	av[3] = 0;
753 
754 	pp = noshell_proc_start(av, (stream *)0, outstream(),  (stream *)0, 1, 0);
755 	if (pp == 0) {
756 		reply("450 4.3.2 We're busy right now, try later\r\n");
757 		return;
758 	}
759 
760 	p = Brdline(pp->std[1]->fp, '\n');
761 	if(p == 0){
762 		reply("550 5.1.0 String does not match anything.\r\n");
763 	} else {
764 		p[Blinelen(pp->std[1]->fp)-1] = 0;
765 		if(strchr(p, ':'))
766 			reply("550 5.1.0  String does not match anything.\r\n");
767 		else{
768 			q = strrchr(p, '!');
769 			if(q)
770 				p = q+1;
771 			reply("250 2.0.0 %s <%s@%s>\r\n", s_to_c(path), p, dom);
772 		}
773 	}
774 	proc_wait(pp);
775 	proc_free(pp);
776 	pp = 0;
777 }
778 
779 /*
780  *  get a line that ends in crnl or cr, turn terminating crnl into a nl
781  *
782  *  return 0 on EOF
783  */
784 static int
getcrnl(String * s,Biobuf * fp)785 getcrnl(String *s, Biobuf *fp)
786 {
787 	int c;
788 
789 	for(;;){
790 		c = Bgetc(fp);
791 		if(debug) {
792 			seek(2, 0, 2);
793 			fprint(2, "%c", c);
794 		}
795 		switch(c){
796 		case 0:
797 			break;
798 		case -1:
799 			goto out;
800 		case '\r':
801 			c = Bgetc(fp);
802 			if(c == '\n'){
803 				if(debug) {
804 					seek(2, 0, 2);
805 					fprint(2, "%c", c);
806 					stamp();
807 				}
808 				s_putc(s, '\n');
809 				goto out;
810 			}
811 			Bungetc(fp);
812 			s_putc(s, '\r');
813 			break;
814 		case '\n':
815 			s_putc(s, c);
816 			goto out;
817 		default:
818 			s_putc(s, c);
819 			break;
820 		}
821 	}
822 out:
823 	s_terminate(s);
824 	return s_len(s);
825 }
826 
827 void
logcall(int nbytes)828 logcall(int nbytes)
829 {
830 	Link *l;
831 	String *to, *from;
832 
833 	to = s_new();
834 	from = s_new();
835 	for(l = senders.first; l; l = l->next){
836 		if(l != senders.first)
837 			s_append(from, ", ");
838 		s_append(from, s_to_c(l->p));
839 	}
840 	for(l = rcvers.first; l; l = l->next){
841 		if(l != rcvers.first)
842 			s_append(to, ", ");
843 		s_append(to, s_to_c(l->p));
844 	}
845 	syslog(0, "smtpd", "[%s/%s] %s sent %d bytes to %s", him, nci->rsys,
846 		s_to_c(from), nbytes, s_to_c(to));
847 	s_free(to);
848 	s_free(from);
849 }
850 
851 static void
logmsg(char * action)852 logmsg(char *action)
853 {
854 	Link *l;
855 
856 	if(logged)
857 		return;
858 
859 	logged = 1;
860 	for(l = rcvers.first; l; l = l->next)
861 		syslog(0, "smtpd", "%s %s (%s/%s) (%s)", action,
862 			s_to_c(senders.last->p), him, nci->rsys, s_to_c(l->p));
863 }
864 
865 static int
optoutall(int filterstate)866 optoutall(int filterstate)
867 {
868 	Link *l;
869 
870 	switch(filterstate){
871 	case ACCEPT:
872 	case TRUSTED:
873 		return filterstate;
874 	}
875 
876 	for(l = rcvers.first; l; l = l->next)
877 		if(!optoutofspamfilter(s_to_c(l->p)))
878 			return filterstate;
879 
880 	return ACCEPT;
881 }
882 
883 String*
startcmd(void)884 startcmd(void)
885 {
886 	int n;
887 	char *filename;
888 	char **av;
889 	Link *l;
890 	String *cmd;
891 
892 	/*
893 	 *  ignore the filterstate if the all the receivers prefer it.
894 	 */
895 	filterstate = optoutall(filterstate);
896 
897 	switch (filterstate){
898 	case BLOCKED:
899 	case DELAY:
900 		rejectcount++;
901 		logmsg("Blocked");
902 		filename = dumpfile(s_to_c(senders.last->p));
903 		cmd = s_new();
904 		s_append(cmd, "cat > ");
905 		s_append(cmd, filename);
906 		pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 0, 0);
907 		break;
908 	case DIALUP:
909 		logmsg("Dialup");
910 		rejectcount++;
911 		reply("554 5.7.1 We don't accept mail from dial-up ports.\r\n");
912 		/*
913 		 * we could exit here, because we're never going to accept mail
914 		 * from this ip address, but it's unclear that RFC821 allows
915 		 * that.  Instead we set the hardreject flag and go stupid.
916 		 */
917 		hardreject = 1;
918 		return 0;
919 	case DENIED:
920 		logmsg("Denied");
921 		rejectcount++;
922 		reply("554-5.7.1 We don't accept mail from %s.\r\n",
923 			s_to_c(senders.last->p));
924 		reply("554 5.7.1 Contact postmaster@%s for more information.\r\n",
925 			dom);
926 		return 0;
927 	case REFUSED:
928 		logmsg("Refused");
929 		rejectcount++;
930 		reply("554 5.7.1 Sender domain must exist: %s\r\n",
931 			s_to_c(senders.last->p));
932 		return 0;
933 	default:
934 	case NONE:
935 		logmsg("Confused");
936 		rejectcount++;
937 		reply("554-5.7.0 We have had an internal mailer error "
938 			"classifying your message.\r\n");
939 		reply("554-5.7.0 Filterstate is %d\r\n", filterstate);
940 		reply("554 5.7.0 Contact postmaster@%s for more information.\r\n",
941 			dom);
942 		return 0;
943 	case ACCEPT:
944 		/*
945 		 * now that all other filters have been passed,
946 		 * do grey-list processing.
947 		 */
948 		if(gflag)
949 			vfysenderhostok();
950 		/* fall through */
951 
952 	case TRUSTED:
953 		/*
954 		 *  set up mail command
955 		 */
956 		cmd = s_clone(mailer);
957 		n = 3;
958 		for(l = rcvers.first; l; l = l->next)
959 			n++;
960 		av = malloc(n * sizeof(char*));
961 		if(av == nil){
962 			reply("450 4.3.2 We're busy right now, try later\r\n");
963 			s_free(cmd);
964 			return 0;
965 		}
966 
967 		n = 0;
968 		av[n++] = s_to_c(cmd);
969 		av[n++] = "-r";
970 		for(l = rcvers.first; l; l = l->next)
971 			av[n++] = s_to_c(l->p);
972 		av[n] = 0;
973 		/*
974 		 *  start mail process
975 		 */
976 		pp = noshell_proc_start(av, instream(), outstream(),
977 			outstream(), 0, 0);
978 		free(av);
979 		break;
980 	}
981 	if(pp == 0) {
982 		reply("450 4.3.2 We're busy right now, try later\r\n");
983 		s_free(cmd);
984 		return 0;
985 	}
986 	return cmd;
987 }
988 
989 /*
990  *  print out a header line, expanding any domainless addresses into
991  *  address@him
992  */
993 char*
bprintnode(Biobuf * b,Node * p,int * cntp)994 bprintnode(Biobuf *b, Node *p, int *cntp)
995 {
996 	int len;
997 
998 	*cntp = 0;
999 	if(p->s){
1000 		if(p->addr && strchr(s_to_c(p->s), '@') == nil){
1001 			if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
1002 				return nil;
1003 			*cntp += s_len(p->s) + 1 + strlen(him);
1004 		} else {
1005 			len = s_len(p->s);
1006 			if(Bwrite(b, s_to_c(p->s), len) < 0)
1007 				return nil;
1008 			*cntp += len;
1009 		}
1010 	}else{
1011 		if(Bputc(b, p->c) < 0)
1012 			return nil;
1013 		++*cntp;
1014 	}
1015 	if(p->white) {
1016 		len = s_len(p->white);
1017 		if(Bwrite(b, s_to_c(p->white), len) < 0)
1018 			return nil;
1019 		*cntp += len;
1020 	}
1021 	return p->end+1;
1022 }
1023 
1024 static String*
getaddr(Node * p)1025 getaddr(Node *p)
1026 {
1027 	for(; p; p = p->next)
1028 		if(p->s && p->addr)
1029 			return p->s;
1030 	return nil;
1031 }
1032 
1033 /*
1034  *  add warning headers of the form
1035  *	X-warning: <reason>
1036  *  for any headers that looked like they might be forged.
1037  *
1038  *  return byte count of new headers
1039  */
1040 static int
forgedheaderwarnings(void)1041 forgedheaderwarnings(void)
1042 {
1043 	int nbytes;
1044 	Field *f;
1045 
1046 	nbytes = 0;
1047 
1048 	/* warn about envelope sender */
1049 	if(senders.last != nil && senders.last->p != nil &&
1050 	    strcmp(s_to_c(senders.last->p), "/dev/null") != 0 &&
1051 	    masquerade(senders.last->p, nil))
1052 		nbytes += Bprint(pp->std[0]->fp,
1053 			"X-warning: suspect envelope domain\n");
1054 
1055 	/*
1056 	 *  check Sender: field.  If it's OK, ignore the others because this
1057 	 *  is an exploded mailing list.
1058 	 */
1059 	for(f = firstfield; f; f = f->next)
1060 		if(f->node->c == SENDER)
1061 			if(masquerade(getaddr(f->node), him))
1062 				nbytes += Bprint(pp->std[0]->fp,
1063 					"X-warning: suspect Sender: domain\n");
1064 			else
1065 				return nbytes;
1066 
1067 	/* check From: */
1068 	for(f = firstfield; f; f = f->next){
1069 		if(f->node->c == FROM && masquerade(getaddr(f->node), him))
1070 			nbytes += Bprint(pp->std[0]->fp,
1071 				"X-warning: suspect From: domain\n");
1072 	}
1073 	return nbytes;
1074 }
1075 
1076 /*
1077  *  pipe message to mailer with the following transformations:
1078  *	- change \r\n into \n.
1079  *	- add sender's domain to any addrs with no domain
1080  *	- add a From: if none of From:, Sender:, or Replyto: exists
1081  *	- add a Received: line
1082  */
1083 int
pipemsg(int * byteswritten)1084 pipemsg(int *byteswritten)
1085 {
1086 	int n, nbytes, sawdot, status, nonhdr, bpr;
1087 	char *cp;
1088 	Field *f;
1089 	Link *l;
1090 	Node *p;
1091 	String *hdr, *line;
1092 
1093 	pipesig(&status);	/* set status to 1 on write to closed pipe */
1094 	sawdot = 0;
1095 	status = 0;
1096 
1097 	/*
1098 	 *  add a 'From ' line as envelope
1099 	 */
1100 	nbytes = 0;
1101 	nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
1102 		s_to_c(senders.first->p), thedate());
1103 
1104 	/*
1105 	 *  add our own Received: stamp
1106 	 */
1107 	nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
1108 	if(nci->rsys)
1109 		nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
1110 	nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
1111 
1112 	/*
1113 	 *  read first 16k obeying '.' escape.  we're assuming
1114 	 *  the header will all be there.
1115 	 */
1116 	line = s_new();
1117 	hdr = s_new();
1118 	while(sawdot == 0 && s_len(hdr) < 16*1024){
1119 		n = getcrnl(s_reset(line), &bin);
1120 
1121 		/* eof or error ends the message */
1122 		if(n <= 0)
1123 			break;
1124 
1125 		/* a line with only a '.' ends the message */
1126 		cp = s_to_c(line);
1127 		if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
1128 			sawdot = 1;
1129 			break;
1130 		}
1131 
1132 		s_append(hdr, *cp == '.' ? cp+1 : cp);
1133 	}
1134 
1135 	/*
1136 	 *  parse header
1137 	 */
1138 	yyinit(s_to_c(hdr), s_len(hdr));
1139 	yyparse();
1140 
1141 	/*
1142 	 *  Look for masquerades.  Let Sender: trump From: to allow mailing list
1143 	 *  forwarded messages.
1144 	 */
1145 	if(fflag)
1146 		nbytes += forgedheaderwarnings();
1147 
1148 	/*
1149 	 *  add an orginator and/or destination if either is missing
1150 	 */
1151 	if(originator == 0){
1152 		if(senders.last == nil || senders.last->p == nil)
1153 			nbytes += Bprint(pp->std[0]->fp, "From: /dev/null@%s\n",
1154 				him);
1155 		else
1156 			nbytes += Bprint(pp->std[0]->fp, "From: %s\n",
1157 				s_to_c(senders.last->p));
1158 	}
1159 	if(destination == 0){
1160 		nbytes += Bprint(pp->std[0]->fp, "To: ");
1161 		for(l = rcvers.first; l; l = l->next){
1162 			if(l != rcvers.first)
1163 				nbytes += Bprint(pp->std[0]->fp, ", ");
1164 			nbytes += Bprint(pp->std[0]->fp, "%s", s_to_c(l->p));
1165 		}
1166 		nbytes += Bprint(pp->std[0]->fp, "\n");
1167 	}
1168 
1169 	/*
1170 	 *  add sender's domain to any domainless addresses
1171 	 *  (to avoid forging local addresses)
1172 	 */
1173 	cp = s_to_c(hdr);
1174 	for(f = firstfield; cp != nil && f; f = f->next){
1175 		for(p = f->node; cp != 0 && p; p = p->next) {
1176 			bpr = 0;
1177 			cp = bprintnode(pp->std[0]->fp, p, &bpr);
1178 			nbytes += bpr;
1179 		}
1180 		if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
1181 			piperror = "write error";
1182 			status = 1;
1183 		}
1184 		nbytes++;		/* for newline */
1185 	}
1186 	if(cp == nil){
1187 		piperror = "sender domain";
1188 		status = 1;
1189 	}
1190 
1191 	/* write anything we read following the header */
1192 	nonhdr = s_to_c(hdr) + s_len(hdr) - cp;
1193 	if(status == 0 && Bwrite(pp->std[0]->fp, cp, nonhdr) < 0){
1194 		piperror = "write error 2";
1195 		status = 1;
1196 	}
1197 	nbytes += nonhdr;
1198 	s_free(hdr);
1199 
1200 	/*
1201 	 *  pass rest of message to mailer.  take care of '.'
1202 	 *  escapes.
1203 	 */
1204 	while(sawdot == 0){
1205 		n = getcrnl(s_reset(line), &bin);
1206 
1207 		/* eof or error ends the message */
1208 		if(n <= 0)
1209 			break;
1210 
1211 		/* a line with only a '.' ends the message */
1212 		cp = s_to_c(line);
1213 		if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
1214 			sawdot = 1;
1215 			break;
1216 		}
1217 		if(cp[0] == '.'){
1218 			cp++;
1219 			n--;
1220 		}
1221 		nbytes += n;
1222 		if(status == 0 && Bwrite(pp->std[0]->fp, cp, n) < 0){
1223 			piperror = "write error 3";
1224 			status = 1;
1225 		}
1226 	}
1227 	s_free(line);
1228 	if(sawdot == 0){
1229 		/* message did not terminate normally */
1230 		snprint(pipbuf, sizeof pipbuf, "network eof: %r");
1231 		piperror = pipbuf;
1232 		if (pp->pid > 0) {
1233 			syskillpg(pp->pid);
1234 			/* none can't syskillpg, so try a variant */
1235 			sleep(500);
1236 			syskill(pp->pid);
1237 		}
1238 		status = 1;
1239 	}
1240 
1241 	if(status == 0 && Bflush(pp->std[0]->fp) < 0){
1242 		piperror = "write error 4";
1243 		status = 1;
1244 	}
1245 	if (debug) {
1246 		stamp();
1247 		fprint(2, "at end of message; %s .\n",
1248 			(sawdot? "saw": "didn't see"));
1249 	}
1250 	stream_free(pp->std[0]);
1251 	pp->std[0] = 0;
1252 	*byteswritten = nbytes;
1253 	pipesigoff();
1254 	if(status && !piperror)
1255 		piperror = "write on closed pipe";
1256 	return status;
1257 }
1258 
1259 char*
firstline(char * x)1260 firstline(char *x)
1261 {
1262 	char *p;
1263 	static char buf[128];
1264 
1265 	strncpy(buf, x, sizeof(buf));
1266 	buf[sizeof(buf)-1] = 0;
1267 	p = strchr(buf, '\n');
1268 	if(p)
1269 		*p = 0;
1270 	return buf;
1271 }
1272 
1273 int
sendermxcheck(void)1274 sendermxcheck(void)
1275 {
1276 	int pid;
1277 	char *cp, *senddom, *user, *who;
1278 	Waitmsg *w;
1279 
1280 	who = s_to_c(senders.first->p);
1281 	if(strcmp(who, "/dev/null") == 0){
1282 		/* /dev/null can only send to one rcpt at a time */
1283 		if(rcvers.first != rcvers.last){
1284 			werrstr("rejected: /dev/null sending to multiple "
1285 				"recipients");
1286 			return -1;
1287 		}
1288 		return 0;
1289 	}
1290 
1291 	if(access("/mail/lib/validatesender", AEXEC) < 0)
1292 		return 0;
1293 
1294 	senddom = strdup(who);
1295 	if((cp = strchr(senddom, '!')) == nil){
1296 		werrstr("rejected: domainless sender %s", who);
1297 		free(senddom);
1298 		return -1;
1299 	}
1300 	*cp++ = 0;
1301 	user = cp;
1302 
1303 	switch(pid = fork()){
1304 	case -1:
1305 		werrstr("deferred: fork: %r");
1306 		return -1;
1307 	case 0:
1308 		/*
1309 		 * Could add an option with the remote IP address
1310 		 * to allow validatesender to implement SPF eventually.
1311 		 */
1312 		execl("/mail/lib/validatesender", "validatesender",
1313 			"-n", nci->root, senddom, user, nil);
1314 		_exits("exec validatesender: %r");
1315 	default:
1316 		break;
1317 	}
1318 
1319 	free(senddom);
1320 	w = wait();
1321 	if(w == nil){
1322 		werrstr("deferred: wait failed: %r");
1323 		return -1;
1324 	}
1325 	if(w->pid != pid){
1326 		werrstr("deferred: wait returned wrong pid %d != %d",
1327 			w->pid, pid);
1328 		free(w);
1329 		return -1;
1330 	}
1331 	if(w->msg[0] == 0){
1332 		free(w);
1333 		return 0;
1334 	}
1335 	/*
1336 	 * skip over validatesender 143123132: prefix from rc.
1337 	 */
1338 	cp = strchr(w->msg, ':');
1339 	if(cp && *(cp+1) == ' ')
1340 		werrstr("%s", cp+2);
1341 	else
1342 		werrstr("%s", w->msg);
1343 	free(w);
1344 	return -1;
1345 }
1346 
1347 void
data(void)1348 data(void)
1349 {
1350 	int status, nbytes;
1351 	char *cp, *ep;
1352 	char errx[ERRMAX];
1353 	Link *l;
1354 	String *cmd, *err;
1355 
1356 	if(rejectcheck())
1357 		return;
1358 	if(senders.last == 0){
1359 		reply("503 2.5.2 Data without MAIL FROM:\r\n");
1360 		rejectcount++;
1361 		return;
1362 	}
1363 	if(rcvers.last == 0){
1364 		reply("503 2.5.2 Data without RCPT TO:\r\n");
1365 		rejectcount++;
1366 		return;
1367 	}
1368 	if(!trusted && sendermxcheck()){
1369 		rerrstr(errx, sizeof errx);
1370 		if(strncmp(errx, "rejected:", 9) == 0)
1371 			reply("554 5.7.1 %s\r\n", errx);
1372 		else
1373 			reply("450 4.7.0 %s\r\n", errx);
1374 		for(l=rcvers.first; l; l=l->next)
1375 			syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s",
1376 				him, nci->rsys, s_to_c(senders.first->p),
1377 				s_to_c(l->p), errx);
1378 		rejectcount++;
1379 		return;
1380 	}
1381 
1382 	cmd = startcmd();
1383 	if(cmd == 0)
1384 		return;
1385 
1386 	reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
1387 	if(debug){
1388 		seek(2, 0, 2);
1389 		stamp();
1390 		fprint(2, "# sent 354; accepting DATA %s\n", thedate());
1391 	}
1392 
1393 
1394 	/*
1395 	 *  allow 145 more minutes to move the data
1396 	 */
1397 	alarm(145*60*1000);
1398 
1399 	status = pipemsg(&nbytes);
1400 
1401 	/*
1402 	 *  read any error messages
1403 	 */
1404 	err = s_new();
1405 	if (debug) {
1406 		stamp();
1407 		fprint(2, "waiting for upas/send to close stderr\n");
1408 	}
1409 	while(s_read_line(pp->std[2]->fp, err))
1410 		;
1411 
1412 	alarm(0);
1413 	atnotify(catchalarm, 0);
1414 
1415 	if (debug) {
1416 		stamp();
1417 		fprint(2, "waiting for upas/send to exit\n");
1418 	}
1419 	status |= proc_wait(pp);
1420 	if(debug){
1421 		seek(2, 0, 2);
1422 		stamp();
1423 		fprint(2, "# %d upas/send status %#ux at %s\n",
1424 			getpid(), status, thedate());
1425 		if(*s_to_c(err))
1426 			fprint(2, "# %d error %s\n", getpid(), s_to_c(err));
1427 	}
1428 
1429 	/*
1430 	 *  if process terminated abnormally, send back error message
1431 	 */
1432 	if(status){
1433 		int code;
1434 		char *ecode;
1435 
1436 		if(strstr(s_to_c(err), "mail refused")){
1437 			syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s",
1438 				him, nci->rsys, s_to_c(senders.first->p),
1439 				s_to_c(cmd), firstline(s_to_c(err)));
1440 			code = 554;
1441 			ecode = "5.0.0";
1442 		} else {
1443 			syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s",
1444 				him, nci->rsys,
1445 				s_to_c(senders.first->p), s_to_c(cmd),
1446 				piperror? "error during pipemsg: ": "",
1447 				piperror? piperror: "",
1448 				piperror? "; ": "",
1449 				pp->waitmsg->msg, firstline(s_to_c(err)));
1450 			code = 450;
1451 			ecode = "4.0.0";
1452 		}
1453 		for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
1454 			*ep++ = 0;
1455 			reply("%d-%s %s\r\n", code, ecode, cp);
1456 		}
1457 		reply("%d %s mail process terminated abnormally\r\n",
1458 			code, ecode);
1459 	} else {
1460 		/*
1461 		 * if a message appeared on stderr, despite good status,
1462 		 * log it.  this can happen if rewrite.in contains a bad
1463 		 * r.e., for example.
1464 		 */
1465 		if(*s_to_c(err))
1466 			syslog(0, "smtpd",
1467 				"%s returned good status, but said: %s",
1468 				s_to_c(mailer), s_to_c(err));
1469 
1470 		if(filterstate == BLOCKED)
1471 			reply("554 5.7.1 we believe this is spam.  "
1472 				"we don't accept it.\r\n");
1473 		else if(filterstate == DELAY)
1474 			reply("450 4.3.0 There will be a delay in delivery "
1475 				"of this message.\r\n");
1476 		else {
1477 			reply("250 2.5.0 sent\r\n");
1478 			logcall(nbytes);
1479 			if(debug){
1480 				seek(2, 0, 2);
1481 				stamp();
1482 				fprint(2, "# %d sent 250 reply %s\n",
1483 					getpid(), thedate());
1484 			}
1485 		}
1486 	}
1487 	proc_free(pp);
1488 	pp = 0;
1489 	s_free(cmd);
1490 	s_free(err);
1491 
1492 	listfree(&senders);
1493 	listfree(&rcvers);
1494 }
1495 
1496 /*
1497  * when we have blocked a transaction based on IP address, there is nothing
1498  * that the sender can do to convince us to take the message.  after the
1499  * first rejection, some spammers continually RSET and give a new MAIL FROM:
1500  * filling our logs with rejections.  rejectcheck() limits the retries and
1501  * swiftly rejects all further commands after the first 500-series message
1502  * is issued.
1503  */
1504 int
rejectcheck(void)1505 rejectcheck(void)
1506 {
1507 	if(rejectcount > MAXREJECTS){
1508 		syslog(0, "smtpd", "Rejected (%s/%s)", him, nci->rsys);
1509 		reply("554 5.5.0 too many errors.  transaction failed.\r\n");
1510 		exits("errcount");
1511 	}
1512 	if(hardreject){
1513 		rejectcount++;
1514 		reply("554 5.7.1 We don't accept mail from dial-up ports.\r\n");
1515 	}
1516 	return hardreject;
1517 }
1518 
1519 /*
1520  *  create abs path of the mailer
1521  */
1522 String*
mailerpath(char * p)1523 mailerpath(char *p)
1524 {
1525 	String *s;
1526 
1527 	if(p == nil)
1528 		return nil;
1529 	if(*p == '/')
1530 		return s_copy(p);
1531 	s = s_new();
1532 	s_append(s, UPASBIN);
1533 	s_append(s, "/");
1534 	s_append(s, p);
1535 	return s;
1536 }
1537 
1538 String *
s_dec64(String * sin)1539 s_dec64(String *sin)
1540 {
1541 	int lin, lout;
1542 	String *sout;
1543 
1544 	lin = s_len(sin);
1545 
1546 	/*
1547 	 * if the string is coming from smtpd.y, it will have no nl.
1548 	 * if it is coming from getcrnl below, it will have an nl.
1549 	 */
1550 	if (*(s_to_c(sin)+lin-1) == '\n')
1551 		lin--;
1552 	sout = s_newalloc(lin+1);
1553 	lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin);
1554 	if (lout < 0) {
1555 		s_free(sout);
1556 		return nil;
1557 	}
1558 	sout->ptr = sout->base + lout;
1559 	s_terminate(sout);
1560 	return sout;
1561 }
1562 
1563 void
starttls(void)1564 starttls(void)
1565 {
1566 	int certlen, fd;
1567 	uchar *cert;
1568 	TLSconn *conn;
1569 
1570 	if (tlscert == nil) {
1571 		reply("500 5.5.1 illegal command or bad syntax\r\n");
1572 		return;
1573 	}
1574 	conn = mallocz(sizeof *conn, 1);
1575 	cert = readcert(tlscert, &certlen);
1576 	if (conn == nil || cert == nil) {
1577 		if (conn != nil)
1578 			free(conn);
1579 		reply("454 4.7.5 TLS not available\r\n");
1580 		return;
1581 	}
1582 	reply("220 2.0.0 Go ahead make my day\r\n");
1583 	conn->cert = cert;
1584 	conn->certlen = certlen;
1585 	fd = tlsServer(Bfildes(&bin), conn);
1586 	if (fd < 0) {
1587 		free(cert);
1588 		free(conn);
1589 		syslog(0, "smtpd", "TLS start-up failed with %s", him);
1590 
1591 		/* force the client to hang up */
1592 		close(Bfildes(&bin));		/* probably fd 0 */
1593 		close(1);
1594 		exits("tls failed");
1595 	}
1596 	Bterm(&bin);
1597 	Binit(&bin, fd, OREAD);
1598 	if (dup(fd, 1) < 0)
1599 		fprint(2, "dup of %d failed: %r\n", fd);
1600 	passwordinclear = 1;
1601 	syslog(0, "smtpd", "started TLS with %s", him);
1602 }
1603 
1604 void
auth(String * mech,String * resp)1605 auth(String *mech, String *resp)
1606 {
1607 	char *user, *pass, *scratch = nil;
1608 	AuthInfo *ai = nil;
1609 	Chalstate *chs = nil;
1610 	String *s_resp1_64 = nil, *s_resp2_64 = nil, *s_resp1 = nil;
1611 	String *s_resp2 = nil;
1612 
1613 	if (rejectcheck())
1614 		goto bomb_out;
1615 
1616 	syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
1617 		"(protected)", him);
1618 
1619 	if (authenticated) {
1620 	bad_sequence:
1621 		rejectcount++;
1622 		reply("503 5.5.2 Bad sequence of commands\r\n");
1623 		goto bomb_out;
1624 	}
1625 	if (cistrcmp(s_to_c(mech), "plain") == 0) {
1626 		if (!passwordinclear) {
1627 			rejectcount++;
1628 			reply("538 5.7.1 Encryption required for requested "
1629 				"authentication mechanism\r\n");
1630 			goto bomb_out;
1631 		}
1632 		s_resp1_64 = resp;
1633 		if (s_resp1_64 == nil) {
1634 			reply("334 \r\n");
1635 			s_resp1_64 = s_new();
1636 			if (getcrnl(s_resp1_64, &bin) <= 0)
1637 				goto bad_sequence;
1638 		}
1639 		s_resp1 = s_dec64(s_resp1_64);
1640 		if (s_resp1 == nil) {
1641 			rejectcount++;
1642 			reply("501 5.5.4 Cannot decode base64\r\n");
1643 			goto bomb_out;
1644 		}
1645 		memset(s_to_c(s_resp1_64), 'X', s_len(s_resp1_64));
1646 		user = s_to_c(s_resp1) + strlen(s_to_c(s_resp1)) + 1;
1647 		pass = user + strlen(user) + 1;
1648 		ai = auth_userpasswd(user, pass);
1649 		authenticated = ai != nil;
1650 		memset(pass, 'X', strlen(pass));
1651 		goto windup;
1652 	}
1653 	else if (cistrcmp(s_to_c(mech), "login") == 0) {
1654 		if (!passwordinclear) {
1655 			rejectcount++;
1656 			reply("538 5.7.1 Encryption required for requested "
1657 				"authentication mechanism\r\n");
1658 			goto bomb_out;
1659 		}
1660 		if (resp == nil) {
1661 			reply("334 VXNlcm5hbWU6\r\n");
1662 			s_resp1_64 = s_new();
1663 			if (getcrnl(s_resp1_64, &bin) <= 0)
1664 				goto bad_sequence;
1665 		}
1666 		reply("334 UGFzc3dvcmQ6\r\n");
1667 		s_resp2_64 = s_new();
1668 		if (getcrnl(s_resp2_64, &bin) <= 0)
1669 			goto bad_sequence;
1670 		s_resp1 = s_dec64(s_resp1_64);
1671 		s_resp2 = s_dec64(s_resp2_64);
1672 		memset(s_to_c(s_resp2_64), 'X', s_len(s_resp2_64));
1673 		if (s_resp1 == nil || s_resp2 == nil) {
1674 			rejectcount++;
1675 			reply("501 5.5.4 Cannot decode base64\r\n");
1676 			goto bomb_out;
1677 		}
1678 		ai = auth_userpasswd(s_to_c(s_resp1), s_to_c(s_resp2));
1679 		authenticated = ai != nil;
1680 		memset(s_to_c(s_resp2), 'X', s_len(s_resp2));
1681 windup:
1682 		if (authenticated) {
1683 			/* if you authenticated, we trust you despite your IP */
1684 			trusted = 1;
1685 			reply("235 2.0.0 Authentication successful\r\n");
1686 		} else {
1687 			rejectcount++;
1688 			reply("535 5.7.1 Authentication failed\r\n");
1689 			syslog(0, "smtpd", "authentication failed: %r");
1690 		}
1691 		goto bomb_out;
1692 	}
1693 	else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
1694 		int chal64n;
1695 		char *resp, *t;
1696 
1697 		chs = auth_challenge("proto=cram role=server");
1698 		if (chs == nil) {
1699 			rejectcount++;
1700 			reply("501 5.7.5 Couldn't get CRAM-MD5 challenge\r\n");
1701 			goto bomb_out;
1702 		}
1703 		scratch = malloc(chs->nchal * 2 + 1);
1704 		chal64n = enc64(scratch, chs->nchal * 2, (uchar *)chs->chal,
1705 			chs->nchal);
1706 		scratch[chal64n] = 0;
1707 		reply("334 %s\r\n", scratch);
1708 		s_resp1_64 = s_new();
1709 		if (getcrnl(s_resp1_64, &bin) <= 0)
1710 			goto bad_sequence;
1711 		s_resp1 = s_dec64(s_resp1_64);
1712 		if (s_resp1 == nil) {
1713 			rejectcount++;
1714 			reply("501 5.5.4 Cannot decode base64\r\n");
1715 			goto bomb_out;
1716 		}
1717 		/* should be of form <user><space><response> */
1718 		resp = s_to_c(s_resp1);
1719 		t = strchr(resp, ' ');
1720 		if (t == nil) {
1721 			rejectcount++;
1722 			reply("501 5.5.4 Poorly formed CRAM-MD5 response\r\n");
1723 			goto bomb_out;
1724 		}
1725 		*t++ = 0;
1726 		chs->user = resp;
1727 		chs->resp = t;
1728 		chs->nresp = strlen(t);
1729 		ai = auth_response(chs);
1730 		authenticated = ai != nil;
1731 		goto windup;
1732 	}
1733 	rejectcount++;
1734 	reply("501 5.5.1 Unrecognised authentication type %s\r\n", s_to_c(mech));
1735 bomb_out:
1736 	if (ai)
1737 		auth_freeAI(ai);
1738 	if (chs)
1739 		auth_freechal(chs);
1740 	if (scratch)
1741 		free(scratch);
1742 	if (s_resp1)
1743 		s_free(s_resp1);
1744 	if (s_resp2)
1745 		s_free(s_resp2);
1746 	if (s_resp1_64)
1747 		s_free(s_resp1_64);
1748 	if (s_resp2_64)
1749 		s_free(s_resp2_64);
1750 }
1751 
1752