plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

smtpd.c (31322B)


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