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 <thread.h>
11 #include "../smtp/rfc822.tab.h"
13 #define DBGMX 1
15 char *me;
16 char *him="";
17 char *dom;
18 process *pp;
19 String *mailer;
20 NetConnInfo *nci;
22 int filterstate = ACCEPT;
23 int trusted;
24 int logged;
25 int rejectcount;
26 int hardreject;
28 Biobuf bin;
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;
41 List senders;
42 List rcvers;
44 char pipbuf[ERRMAX];
45 char *piperror;
46 int pipemsg(int*);
47 String* startcmd(void);
48 int rejectcheck(void);
49 String* mailerpath(char*);
51 static int
52 catchalarm(void *a, char *msg)
53 {
54 int rv = 1;
56 USED(a);
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 }
68 /* kill the children if there are any */
69 if(pp)
70 syskillpg(pp->pid);
72 return rv;
73 }
75 /* override string error functions to do something reasonable */
76 void
77 s_error(char *f, char *status)
78 {
79 char errbuf[Errlen];
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 }
91 void
92 threadmain(int argc, char **argv)
93 {
94 char *p, buf[1024];
95 char *netdir;
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;
154 nci = getnetconninfo(netdir, 0);
155 if(nci == nil)
156 sysfatal("can't get remote system's address");
158 if(mailer == nil)
159 mailer = mailerpath("send");
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;
170 getconf();
171 Binit(&bin, 0, OREAD);
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);
188 void
189 listfree(List *l)
191 Link *lp;
192 Link *next;
194 for(lp = l->first; lp; lp = next){
195 next = lp->next;
196 s_free(lp->p);
197 free(lp);
199 l->first = l->last = 0;
202 void
203 listadd(List *l, String *path)
205 Link *lp;
207 lp = (Link *)malloc(sizeof(Link));
208 lp->p = path;
209 lp->next = 0;
211 if(l->last)
212 l->last->next = lp;
213 else
214 l->first = lp;
215 l->last = lp;
218 #define SIZE 4096
219 int
220 reply(char *fmt, ...)
222 char buf[SIZE], *out;
223 va_list arg;
224 int n;
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);
234 write(1, buf, n);
235 return n;
238 void
239 reset(void)
241 if(rejectcheck())
242 return;
243 listfree(&rcvers);
244 listfree(&senders);
245 if(filterstate != DIALUP){
246 logged = 0;
247 filterstate = ACCEPT;
249 reply("250 ok\r\n");
252 void
253 sayhi(void)
255 reply("220 %s SMTP\r\n", dom);
258 void
259 hello(String *himp, int extended)
261 char **mynames;
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;
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;
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;
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");
314 void
315 sender(String *path)
317 String *s;
318 static char *lastsender;
320 if(rejectcheck())
321 return;
322 if (authenticate && !authenticated) {
323 rejectcount++;
324 reply("530 Authentication required\r\n");
325 return;
327 if(him == 0 || *him == 0){
328 rejectcount++;
329 reply("503 Start by saying HELO, please.\r\n", s_to_c(path));
330 return;
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;
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;
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;
360 free(lastsender); /* different sender domain */
361 lastsender = 0;
364 /*
365 * see if this ip address, domain name, user name or account is blocked
366 */
367 filterstate = blocked(path);
369 logged = 0;
370 listadd(&senders, path);
371 reply("250 sender is %s\r\n", s_to_c(path));
374 enum { Rcpt, Domain, Ntoks };
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];
385 static int
386 rdsenders(void)
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;
395 if (beenhere)
396 return 1;
397 beenhere = 1;
399 fmtinstall('I', eipfmt);
400 parseip(rsysip, nci->rsys);
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 */
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;
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]);
436 Bterm(sf);
437 return ok;
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)
448 int mentioned = 0, matched = 0;
449 uchar dnsip[IPaddrlen];
450 Sender *snd;
451 Ndbtuple *nt, *next, *first;
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);
482 if (matched)
483 return 1;
484 else
485 return !mentioned;
488 void
489 receiver(String *path)
491 char *sender, *rcpt;
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;
500 if(senders.last)
501 sender = s_to_c(senders.last->p);
502 else
503 sender = "<unknown>";
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;
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;
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;
530 listadd(&rcvers, path);
531 reply("250 receiver is %s\r\n", s_to_c(path));
534 void
535 quit(void)
537 reply("221 Successful termination\r\n");
538 close(0);
539 threadexitsall(0);
542 void
543 turn(void)
545 if(rejectcheck())
546 return;
547 reply("502 TURN unimplemented\r\n");
550 void
551 noop(void)
553 if(rejectcheck())
554 return;
555 reply("250 Stop wasting my time!\r\n");
558 void
559 help(String *cmd)
561 if(rejectcheck())
562 return;
563 if(cmd)
564 s_free(cmd);
565 reply("250 Read rfc821 and stop wasting my time\r\n");
568 void
569 verify(String *path)
571 char *p, *q;
572 char *av[4];
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;
580 av[0] = s_to_c(mailer);
581 av[1] = "-x";
582 av[2] = s_to_c(path);
583 av[3] = 0;
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;
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);
605 proc_wait(pp);
606 proc_free(pp);
607 pp = 0;
610 /*
611 * get a line that ends in crnl or cr, turn terminating crnl into a nl
613 * return 0 on EOF
614 */
615 static int
616 getcrnl(String *s, Biobuf *fp)
618 int c;
620 for(;;){
621 c = Bgetc(fp);
622 if(debug) {
623 seek(2, 0, 2);
624 fprint(2, "%c", c);
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);
636 s_putc(s, '\n');
637 goto out;
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;
650 out:
651 s_terminate(s);
652 return s_len(s);
655 void
656 logcall(int nbytes)
658 Link *l;
659 String *to, *from;
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));
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));
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);
679 static void
680 logmsg(char *action)
682 Link *l;
684 if(logged)
685 return;
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));
693 static int
694 optoutall(int filterstate)
696 Link *l;
698 switch(filterstate){
699 case ACCEPT:
700 case TRUSTED:
701 return filterstate;
704 for(l = rcvers.first; l; l = l->next)
705 if(!optoutofspamfilter(s_to_c(l->p)))
706 return filterstate;
708 return ACCEPT;
711 String*
712 startcmd(void)
714 int n;
715 Link *l;
716 char **av;
717 String *cmd;
718 char *filename;
720 /*
721 * ignore the filterstate if the all the receivers prefer it.
722 */
723 filterstate = optoutall(filterstate);
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();
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;
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;
802 if(pp == 0) {
803 reply("450 We're busy right now, try later\n");
804 s_free(cmd);
805 return 0;
807 return cmd;
810 /*
811 * print out a header line, expanding any domainless addresses into
812 * address@him
813 */
814 char*
815 bprintnode(Biobuf *b, Node *p)
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;
825 }else{
826 if(Bputc(b, p->c) < 0)
827 return nil;
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;
835 static String*
836 getaddr(Node *p)
838 for(; p; p = p->next)
839 if(p->s && p->addr)
840 return p->s;
841 return nil;
844 /*
845 * add waring headers of the form
846 * X-warning: <reason>
847 * for any headers that looked like they might be forged.
849 * return byte count of new headers
850 */
851 static int
852 forgedheaderwarnings(void)
854 int nbytes;
855 Field *f;
857 nbytes = 0;
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");
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;
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");
881 return nbytes;
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)
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;
904 pipesig(&status); /* set status to 1 on write to closed pipe */
905 sawdot = 0;
906 status = 0;
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());
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());
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);
932 /* eof or error ends the message */
933 if(n <= 0)
934 break;
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;
943 s_append(hdr, *cp == '.' ? cp+1 : cp);
946 /*
947 * parse header
948 */
949 yyinit(s_to_c(hdr), s_len(hdr));
950 yyparse();
952 /*
953 * Look for masquerades. Let Sender: trump From: to allow mailing list
954 * forwarded messages.
955 */
956 if(fflag)
957 nbytes += forgedheaderwarnings();
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));
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));
975 Bprint(pp->std[0]->fp, "\n");
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;
991 if(cp == nil){
992 piperror = "sender domain";
993 status = 1;
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;
1001 s_free(hdr);
1004 * pass rest of message to mailer. take care of '.'
1005 * escapes.
1007 while(sawdot == 0){
1008 n = getcrnl(s_reset(line), &bin);
1010 /* eof or error ends the message */
1011 if(n <= 0)
1012 break;
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;
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;
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;
1035 if(status == 0 && Bflush(pp->std[0]->fp) < 0){
1036 piperror = "write error 4";
1037 status = 1;
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;
1048 char*
1049 firstline(char *x)
1051 static char buf[128];
1052 char *p;
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;
1062 int
1063 sendermxcheck(void)
1065 char *cp, *senddom, *user;
1066 char *who;
1067 int pid;
1068 Waitmsg *w;
1069 static char *validate;
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;
1078 return 0;
1081 if(validate == nil)
1082 validate = unsharp("#9/mail/lib/validatesender");
1083 if(access(validate, AEXEC) < 0)
1084 return 0;
1086 senddom = strdup(who);
1087 if((cp = strchr(senddom, '!')) == nil){
1088 werrstr("rejected: domainless sender %s", who);
1089 free(senddom);
1090 return -1;
1092 *cp++ = 0;
1093 user = cp;
1095 switch(pid = fork()){
1096 case -1:
1097 werrstr("deferred: fork: %r");
1098 return -1;
1099 case 0:
1101 * Could add an option with the remote IP address
1102 * to allow validatesender to implement SPF eventually.
1104 execl(validate, "validatesender",
1105 "-n", nci->root, senddom, user, nil);
1106 threadexitsall("exec validatesender: %r");
1107 default:
1108 break;
1111 free(senddom);
1112 w = wait();
1113 if(w == nil){
1114 werrstr("deferred: wait failed: %r");
1115 return -1;
1117 if(w->pid != pid){
1118 werrstr("deferred: wait returned wrong pid %d != %d", w->pid, pid);
1119 free(w);
1120 return -1;
1122 if(w->msg[0] == 0){
1123 free(w);
1124 return 0;
1127 * skip over validatesender 143123132: prefix from rc.
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;
1138 void
1139 data(void)
1141 String *cmd;
1142 String *err;
1143 int status, nbytes;
1144 char *cp, *ep;
1145 char errx[ERRMAX];
1146 Link *l;
1148 if(rejectcheck())
1149 return;
1150 if(senders.last == 0){
1151 reply("503 Data without MAIL FROM:\r\n");
1152 rejectcount++;
1153 return;
1155 if(rcvers.last == 0){
1156 reply("503 Data without RCPT TO:\r\n");
1157 rejectcount++;
1158 return;
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;
1174 cmd = startcmd();
1175 if(cmd == 0)
1176 return;
1178 reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
1181 * allow 145 more minutes to move the data
1183 alarm(145*60*1000);
1185 status = pipemsg(&nbytes);
1188 * read any error messages
1190 err = s_new();
1191 while(s_read_line(pp->std[2]->fp, err))
1194 alarm(0);
1195 atnotify(catchalarm, 0);
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));
1206 * if process terminated abnormally, send back error message
1208 if(status){
1209 int code;
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;
1224 for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
1225 *ep++ = 0;
1226 reply("%d-%s\r\n", code, cp);
1228 reply("%d mail process terminated abnormally\r\n", code);
1229 } else {
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.
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));
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);
1250 proc_free(pp);
1251 pp = 0;
1252 s_free(cmd);
1253 s_free(err);
1255 listfree(&senders);
1256 listfree(&rcvers);
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.
1267 int
1268 rejectcheck(void)
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");
1276 if(hardreject){
1277 rejectcount++;
1278 reply("554 We don't accept mail from dial-up ports.\r\n");
1280 return hardreject;
1284 * create abs path of the mailer
1286 String*
1287 mailerpath(char *p)
1289 String *s;
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;
1302 String *
1303 s_dec64(String *sin)
1305 String *sout;
1306 int lin, lout;
1307 lin = s_len(sin);
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.
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;
1321 sout->ptr = sout->base + lout;
1322 s_terminate(sout);
1323 return sout;
1326 void
1327 starttls(void)
1329 uchar *cert;
1330 int certlen, fd;
1331 TLSconn *conn;
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;
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);
1350 /* force the client to hang up */
1351 close(Bfildes(&bin)); /* probably fd 0 */
1352 close(1);
1353 threadexitsall("tls failed");
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);
1363 void
1364 auth(String *mech, String *resp)
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;
1375 if (rejectcheck())
1376 goto bomb_out;
1378 syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
1379 "(protected)", him);
1381 if (authenticated) {
1382 bad_sequence:
1383 rejectcount++;
1384 reply("503 Bad sequence of commands\r\n");
1385 goto bomb_out;
1387 if (cistrcmp(s_to_c(mech), "plain") == 0) {
1389 if (!passwordinclear) {
1390 rejectcount++;
1391 reply("538 Encryption required for requested authentication mechanism\r\n");
1392 goto bomb_out;
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;
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;
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;
1416 else if (cistrcmp(s_to_c(mech), "login") == 0) {
1418 if (!passwordinclear) {
1419 rejectcount++;
1420 reply("538 Encryption required for requested authentication mechanism\r\n");
1421 goto bomb_out;
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;
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;
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");
1451 goto bomb_out;
1453 else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
1454 char *resp;
1455 int chal64n;
1456 char *t;
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;
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;
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;
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;
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);