Blob


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