1 /* $Id$ */
2 
3 /*
4  *
5  * Copyright (C) 2004 David Mazieres (dm@uun.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  * USA
21  *
22  */
23 
24 #include "asmtpd.h"
25 #include "rawnet.h"
26 #include "serial.h"
27 
28 #ifdef STARTTLS
29 #include "async_ssl.h"
30 #define AIOS aiossl
31 #else /* !STARTTLS */
32 #define AIOS aios
33 #endif /* !STARTTLS */
34 
35 struct smtp_dispatch {
36   const char *cmd_name;
37   void (smtpd::*const cmd_fn) (str /*cmd*/, str /*arg*/);
38   ihash_entry<smtp_dispatch> link;
39 
40   smtp_dispatch (const char *name, void (smtpd::*fn) (str, str));
41 private:
42   PRIVDEST ~smtp_dispatch ();	// No deleting allowed
43 };
44 
45 const str smtpd::okstr ("250 ok\r\n");
46 u_int smtpd::num_smtpd;
47 u_int smtpd::num_indata;
48 static ihash<const char *, smtp_dispatch, &smtp_dispatch::cmd_name,
49 	     &smtp_dispatch::link> dispatch_tab;
50 static rxx cmdarg ("^(\\S+)(\\s*|\\s+(\\S.*))$");
51 list<smtpd, &smtpd::link> smtplist;
52 
53 bool
tmperr(int err)54 smtpd::tmperr (int err)
55 {
56   switch (err) {
57   case 0:
58   case EINTR:
59   case EIO:
60   case ENOMEM:
61   case ENFILE:
62   case EMFILE:
63   case ENOSPC:
64 #ifdef EPROCLIM
65   case EPROCLIM:
66 #endif /* EPROCLIM */
67   case EDQUOT:
68   case ESTALE:
69   case EAGAIN:
70     return true;
71   default:
72     return false;
73   }
74 }
75 
76 str
line_wrap(str in)77 smtpd::line_wrap (str in)
78 {
79   if (in.len () < 79)
80     return in;
81   strbuf sb;
82   const char *p = in.cstr () + 79;
83   const char *const e = in.cstr () + in.len ();
84   while (p > in.cstr () && !isspace (p[-1]))
85     p--;
86   while (p > in.cstr () && isspace (p[-1]))
87     p--;
88   if (p == in.cstr ()) {
89     p = in.cstr () + 79;
90     while (p < e && !isspace (*p))
91       p++;
92   }
93 
94   sb.tosuio ()->print (in.cstr (), p - in.cstr ());
95   sb << "\n";
96   while (p < e && isspace (*p))
97     p++;
98 
99   for (;;) {
100     const char *q = p + min<ptrdiff_t> (e - p, 75);
101     if (q != e)
102       while (q > p && !isspace (q[-1]))
103 	q--;
104     while (q > p && isspace (q[-1]))
105       q--;
106     if (q == p) {
107       q = p + min<ptrdiff_t> (e - p, 75);
108       while (q < e && !isspace (*q))
109 	q++;
110     }
111     if (q == p)
112       return sb;
113     sb << "    ";
114     sb.tosuio ()->print (p, q - p);
115     sb << "\n";
116     p = q;
117     while (p < e && isspace (*p))
118       p++;
119   }
120 }
121 
122 str
datestr(bool includezone)123 smtpd::datestr (bool includezone)
124 {
125   char buf[80];
126   time_t now;
127   int n;
128 
129   time (&now);
130   if (includezone)
131     n = strftime (buf, sizeof (buf) - 1, "%a, %d %b %Y %H:%M:%S %z (%Z)",
132 		  localtime (&now));
133   else
134     n = strftime (buf, sizeof (buf) - 1, "%a %b %e %Y %H:%M:%S",
135 		  localtime (&now));
136   assert (n);
137   return buf;
138 }
139 
140 void
dispatch_tab_init()141 smtpd::dispatch_tab_init ()
142 {
143   assert (dispatch_tab.size () == 0);
144 #define mkdispatch(name) vNew smtp_dispatch (#name, &smtpd::cmd_##name)
145   mkdispatch (rset);
146   mkdispatch (mail);
147   mkdispatch (rcpt);
148   mkdispatch (data);
149   mkdispatch (vrfy);
150   mkdispatch (auth);
151 #undef mkdispatch
152 }
153 
154 str
msgid_gen()155 smtpd::msgid_gen ()
156 {
157   enum { nwords = 4 };
158   u_int32_t words[nwords];
159   for (int i = 0; i < nwords; i++)
160     words[i] = arandom ();
161   return armor32 (words, sizeof (words));
162 }
163 
164 void
reset()165 smtpd::reset ()
166 {
167   fromaddr = NULL;
168   msgid = msgid_gen ();
169   toaddr.clear ();
170   mxl = NULL;
171   body_set = false;
172   body_user = NULL;
173   body_cmd = NULL;
174   mask_spf = false;
175   spfr = SPF_PASS;
176   spf_expl = NULL;
177   spf_mech = NULL;
178   bounce_res = NULL;
179   mail_error = NULL;
180 }
181 
182 str
received()183 smtpd::received ()
184 {
185   strbuf r;
186 
187   r << "From " << (fromaddr.len () ? fromaddr.cstr () : "MAILER-DAEMON")
188     << " " << datestr (false) << "\n";
189 
190   r << "Received: ";
191   if (helohost)
192     r << "from " << helohost
193       << " (" << name << " [" << inet_ntoa (ipaddr) << "])\n";
194   else
195     r << "(from " << name << " [" << inet_ntoa (ipaddr) << "])\n";
196 
197   if (auth_user)
198     r << "    (authenticated-user " << auth_user << ")\n";
199 #ifdef STARTTLS
200   received_starttls (r);
201 #endif /* STARTTLS */
202   r << "    by " << opt->hostname << " with SMTP id " << msgid << ";\n";
203   if (toaddr.size () == 1)
204     r << "    for " << toaddr[0] << ";\n";
205   r << "    " << datestr () << "\n";
206   r << "    (envelope-from "
207     << (fromaddr.len () ? fromaddr : str ("<>")) << ")\n";
208 
209   if (trust < TRUST_MAIL && !mask_spf) {
210     strbuf sr ("Received-SPF: %s", spf1_print (spfr));
211     sr << "; receiver=" << opt->hostname
212        << "; client-ip=" << inet_ntoa (ipaddr)
213        << "; envelope-from=<" << fromaddr << ">"
214        << "; helo=" << helohost;
215     if (spf_mech)
216       sr << "; mechanism=" << spf_mech;
217     sr << "\n";
218     r << line_wrap (sr);
219   }
220 
221   if (trust <= TRUST_AUTH) {
222     strbuf xa;
223     xa << "X-Avenger: version=" << VERSION
224        << "; receiver=" << opt->hostname;
225     xa << "; client-ip=" << inet_ntoa (ipaddr);
226     xa.fmt ("; client-port=%d", tcpport);
227     size_t len = xa.tosuio ()->resid ();
228     if (dns_error)
229       xa.fmt ("; client-dnsfail=") << dns_error;
230     if (bounce_res)
231       xa << "; bounce-res=" << bounce_res;
232     if (!synfp && synfpc) {
233       sockaddr_in sin;
234       sin.sin_family = AF_INET;
235       sin.sin_port = htons (tcpport);
236       sin.sin_addr = ipaddr;
237 #if USE_SYNFP
238       synfp = synfpc->lookup (sin);
239 #endif /* USE_SYNFP */
240     }
241     if (synfp) {
242       xa << "; syn-fingerprint=" << synfp;
243       if (osguess)
244 	xa << " " << osguess;
245     }
246     if (pipelining)
247       xa << "; eager-pipelining";
248     if (colonspace)
249       xa << "; colon-space";
250     if (post)
251       xa << "; post";
252     // xa.fmt ("; data-bytes=%ld", long (data_msgsize));
253     if (ii && ii->nhops > 0)
254       xa.fmt ("; network-hops=%d", ii->nhops);
255     if (ii && ii->netpath) {
256       xa << "; network-path=" << ii->netpath;
257       xa << "; network-path-time=" << ii->netpath_time;
258     }
259     if (rblenv && !rblenv->results.empty ()) {
260       rbl_status::result *rp = rblenv->results.base ();
261       xa << "; RBL=" << (rp++)->tostr (false);
262       while (rp < rblenv->results.lim ())
263 	xa << ", " << (rp++)->tostr (false);
264     }
265     if (rblenv && !rblenv->errors.empty ()) {
266       rbl_status::rblerr *ep = rblenv->errors.base ();
267       xa << "; RBL-errors=" << ep->name << " ("
268 	 << dns_strerror (ep->error) << ")";
269       while (++ep < rblenv->errors.lim ())
270       xa << ", " << ep->name << " ("
271 	 << dns_strerror (ep->error) << ")";
272     }
273     if (xa.tosuio ()->resid () > len)
274       r << line_wrap (xa);
275   }
276 
277   return r;
278 }
279 
280 void
cmd_mail(str cmd,str arg)281 smtpd::cmd_mail (str cmd, str arg)
282 {
283   str addr = extract_addr (arg, "from:");
284   if (addr && arg[5] == ' ')
285     colonspace = true;
286   if (!addr)
287     respond ("501 syntax error\r\n");
288   else if (fromaddr)
289     respond ("503 sender already set\r\n");
290   else if (trust < TRUST_MAIL && addr.len ()) {
291     str relay = extract_domain (addr);
292     if (!relay)
293       respond ("501 syntax error\r\n");
294     else if (!rblcon) {
295       rblenv = NULL;
296       cmd_mail_2 (addr);
297     }
298     else {
299       rblenv = New refcounted<rbl_status> (*rblcon);
300       rbl_check_env (rblenv, opt->rbls, relay,
301 		     wrap (this, &smtpd::cmd_mail_2, addr));
302     }
303   }
304   else if (trust < TRUST_MAIL && rblcon) {
305     rblenv = New refcounted<rbl_status> (*rblcon);
306     cmd_mail_2 (addr);
307   }
308   else {
309     fromaddr = addr;
310     respond (okstr);
311   }
312 }
313 void
cmd_mail_2(str addr)314 smtpd::cmd_mail_2 (str addr)
315 {
316   if (rblenv && rblenv->score > 100) {
317     strbuf sb;
318     sb.fmt ("550-You cannot send email to this server because you have\r\n"
319 	    "550-been listed in the following RBL domain%s:\r\n",
320 	    rblenv->results.size () == 1 ? "" : "s");
321     for (rbl_status::result *rp = rblenv->results.base ();
322 	 rp < rblenv->results.lim (); rp++)
323       sb << "550-  " << rp->tostr (false) << "\r\n";
324     sb << "550 \r\n";
325     mail_error = sb;
326     fromaddr = addr;
327     respond (okstr);
328   }
329   else if (!addr.len ()) {
330     fromaddr = addr;
331     respond (okstr);
332   }
333   else
334     spf_check (ipaddr, addr, wrap (this, &smtpd::cmd_mail_3, addr),
335 	       helohost, ptr_cache);
336 }
337 void
cmd_mail_3(str addr,spf_result res,str expl,str mech)338 smtpd::cmd_mail_3 (str addr, spf_result res, str expl, str mech)
339 {
340   spfr = res;
341   spf_expl = expl;
342   spf_mech = mech;
343   if (spfr != SPF_FAIL && spfr != SPF_ERROR && opt->smtpcb) {
344     vrfy (myipaddr, addr, ipaddr, wrap (this, &smtpd::cmd_mail_4, addr));
345     return;
346   }
347   else if (spfr == SPF_FAIL) {
348     if (expl && expl.len () && !strchr (expl, '\n'))
349       mail_error = strbuf ()
350 	<< "550-IP address unauthorized to send from that address\r\n"
351 	<< "550 " << expl << "\r\n";
352     else
353       mail_error = "550 IP address unauthorized to send from that address\r\n";
354   }
355   else if (spfr == SPF_ERROR) {
356     if (expl && expl.len () && !strchr (expl, '\n'))
357       mail_error = strbuf ()
358 	<< "451-Temporary DNS error while processing SPF record\r\n"
359 	<< "451 " << expl << "\r\n";
360     else
361       mail_error = "451 Temporary DNS error while processing SPF record\r\n";
362   }
363   cmd_mail_4 (addr, NULL, NULL);
364 }
365 void
cmd_mail_4(str addr,str err,ptr<mxlist> m)366 smtpd::cmd_mail_4 (str addr, str err, ptr<mxlist> m)
367 {
368   if (err) {
369     str code = substr (err, 0, 3);
370     bounce_res = code;
371     if (opt->smtpcb >= 2)
372       mail_error = strbuf ()
373 	<< code << "-There is a problem with sender <" << addr << ">.\r\n"
374 	<< code << "-It is not possible to send bounce messages"
375 	<< " to that address.\r\n"
376 	<< err;
377   }
378   else
379     mxl = m;
380   fromaddr = addr;
381   respond (okstr);
382 }
383 
384 void
cmd_rcpt_0(str cmd,str arg,int,in_addr *,int)385 smtpd::cmd_rcpt_0 (str cmd, str arg, int, in_addr *, int)
386 {
387   cmd_rcpt (cmd, arg);
388 }
389 void
cmd_rcpt(str cmd,str arg)390 smtpd::cmd_rcpt (str cmd, str arg)
391 {
392   if (!mail_error && dns_error && opt->allow_dnsfail < 2)
393     mail_error = strbuf ()
394       << "451-Temporary DNS error while resolving client\r\n"
395       << "451 " << dns_error << "\r\n";
396 
397   if (ii && ii->trp) {
398     netpath_addcb (ii->trp, wrap (this, &smtpd::cmd_rcpt_0, cmd, arg));
399     return;
400   }
401   str addr = extract_addr (arg, "to:");
402   if (addr && arg[5] == ' ')
403     colonspace = true;
404   str relay;
405   if (!addr || !(relay = extract_domain (addr)))
406     respond ("501 syntax error\r\n");
407   else if (!fromaddr)
408     respond ("503 must follow mail\r\n");
409   else if (toaddr.size () >= 0x10000)
410     respond ("452 too many recipients\r\n");
411   else if (trust >= TRUST_AUTH)
412     cmd_rcpt_6 (addr, NULL);
413   else if (addr[0] == '@')
414     respond ("553 multi-hop forward paths disallowed\r\n");
415   else if (!opt->allow_percent && strchr (addr, '%'))
416     respond ("553 \"%\" character disallowed in recipients\r\n");
417   else if (toaddr.size () >= opt->max_rcpts) {
418     if (toaddr.size () >= opt->max_relay_rcpts)
419       respond ("452 too many recipients\r\n");
420     else
421       cmd_rcpt_3 (addr, "452 too many recipients\n");
422   }
423   else if (opt->nocheck[mytolower (addr)])
424     cmd_rcpt_6 (addr, bodycheck (NULL, NULL, NULL));
425   else {
426     int indmap = dmap.hasentry (addr);
427     if (indmap < 0)
428       respond ("451 temporary error processing domain file\r\n");
429     else if (!indmap)
430       ismxlocal (extract_domain (addr),
431 		 wrap (this, &smtpd::cmd_rcpt_2, addr));
432     else if (opt->nocheck[mytolower (extract_local (addr))])
433       cmd_rcpt_6 (addr, bodycheck (NULL, NULL, NULL));
434     else
435       rcptcheck (this, addr, 'r',
436 		 wrap (this, &smtpd::cmd_rcpt_5, addr, str (NULL)));
437   }
438 }
439 void
cmd_rcpt_2(str addr,int err)440 smtpd::cmd_rcpt_2 (str addr, int err)
441 {
442   if (err > 0 && dns_tmperr (err))
443     respond ("451 temporary DNS error\r\n");
444   else if (err <= 0 && opt->mxlocal_rcpt) {
445     if (opt->nocheck[mytolower (extract_local (addr))])
446       cmd_rcpt_6 (addr, bodycheck (NULL, NULL, NULL));
447     else
448       rcptcheck (this, addr, err < 0 ? 'R' : 'r',
449 		 wrap (this, &smtpd::cmd_rcpt_5, addr, str (NULL)));
450   }
451   else {
452     str errmsg;
453     if (err <= 0) {
454       maybe_warn (strbuf () <<"unauthorized MX record "
455 		  << extract_domain (addr) << "\n");
456       errmsg = "451 not relaying for unauthorized MX record\r\n";
457     }
458     else
459       errmsg = "550 relaying denied\r\n";
460     cmd_rcpt_3 (addr, errmsg);
461   }
462 }
463 void
cmd_rcpt_3(str addr,str errmsg)464 smtpd::cmd_rcpt_3 (str addr, str errmsg)
465 {
466   int indmap = 1;
467   if (fromaddr.len ())
468     indmap = dmap.hasentry (fromaddr);
469   if (indmap < 0)
470     respond ("451 temporary error processing domain file\r\n");
471   else if (!indmap && opt->mxlocal_rcpt)
472     ismxlocal (extract_domain (fromaddr),
473 	       wrap (this, &smtpd::cmd_rcpt_4, addr, errmsg));
474   else
475     cmd_rcpt_4 (addr, errmsg, indmap ? 0 : NXDOMAIN);
476 }
477 void
cmd_rcpt_4(str addr,str errmsg,int local)478 smtpd::cmd_rcpt_4 (str addr, str errmsg, int local)
479 {
480   if (opt->mxlocal_rcpt && local > 0 && dns_tmperr (local))
481     respond ("451 temporary DNS error\r\n");
482   else
483     rcptcheck (this, addr, (local || !fromaddr.len ()) ? 'M': 'm',
484 	       wrap (this, &smtpd::cmd_rcpt_5, addr, errmsg));
485 }
486 void
cmd_rcpt_5(str addr,str errmsg,str err)487 smtpd::cmd_rcpt_5 (str addr, str errmsg, str err)
488 {
489   if (errmsg && !err)
490     respond (errmsg);
491   else if (err && err[0] != '2' && !errmsg)
492     cmd_rcpt_3 (addr, err);
493   else if (mail_error && !errmsg && !err)
494     cmd_rcpt_3 (addr, mail_error);
495   else if (errmsg && err && err[0] == '5' && errmsg[0] == '4')
496     cmd_rcpt_6 (addr, errmsg);
497   else {
498     if (errmsg && err && err[0] == '2')
499       mask_spf = true;
500     cmd_rcpt_6 (addr, err);
501   }
502 }
503 void
cmd_rcpt_6(str addr,str err)504 smtpd::cmd_rcpt_6 (str addr, str err)
505 {
506   str rerr;
507   if (err && err[0] != '2')
508     respond (err, fromaddr.len ());
509   else if (ii && (rerr = ii->rcpt ()))
510     respond (rerr);
511   else if (quota_user && (err = userinfo::rcpt (quota_user)))
512     respond (rerr);
513 #if 0
514   /* Just for testing -- fromaddr not same namespace as quota_user */
515   else if (!quota_user && fromaddr.len ()
516 	   && (err = userinfo::rcpt (fromaddr)))
517     respond (rerr);
518 #endif
519   else {
520     toaddr.push_back (addr);
521     respond (err ? err : str (okstr));
522   }
523 }
524 
525 void
cmd_data(str cmd,str arg)526 smtpd::cmd_data (str cmd, str arg)
527 {
528   if (!fromaddr || !toaddr.size ()) {
529     respond ("503 must follow rcpt\r\n");
530     return;
531   }
532 
533   data_state = NEWLINE;
534   data_msgsize = 0;
535   data_err = NULL;
536 
537   assert (!data_q);
538   data_q = New enqmsg_file;
539   if (!data_q->init  (fromaddr, toaddr, received ())) {
540     delete data_q;
541     data_q = NULL;
542     respond ("451 unable to initialize message\r\n");
543     return;
544   }
545 
546   num_indata++;
547   aio->settimeout (opt->data_timeout);
548 
549   aio << "354 enter mail, end with \".\" on a line by itself\r\n";
550   aio->readany (wrap (this, &smtpd::data_1));
551 }
552 
553 void
data_1(str data,int err)554 smtpd::data_1 (str data, int err)
555 {
556   if (!data) {
557     delete data_q;
558     data_q = NULL;
559     num_indata--;
560     delete this;
561     return;
562   }
563 
564   suio newdat;
565   const char *p, *np, *const eom = data.cstr () + data.len ();
566   int ll;
567   bool end = false;
568 
569   for (p = data;
570        (np = static_cast<const char *> (memchr (p, '\n', eom - p)));
571        p = np + 1) {
572     switch (data_state) {
573     case MIDLINE:
574       break;
575     case NEWLINE:
576       if (p[0] == '.') {
577 	if (p[1] == '\n' || (p[1] == '\r' && p[2] == '\n')) {
578 	  end = true;
579 	  goto done;
580 	}
581 	else if (p[1] == '.')
582 	  p++;
583       }
584       break;
585     case CR:
586       if (p[0] != '\n')
587 	newdat.print ("\r", 1);
588       break;
589     case DOT:
590       if (p[0] == '\n' || (p[0] == '\r' && p[1] == '\n')) {
591 	end = true;
592 	goto done;
593       }
594       else if (p[0] != '.')
595 	newdat.print (".", 1);
596       break;
597     case DOTCR:
598       if (p[0] == '\n') {
599 	end = true;
600 	goto done;
601       }
602       newdat.print (".\r", 2);
603       break;
604     }
605     data_state = NEWLINE;
606     ll = np - p;
607     if (ll > 0 && np[-1] == '\r')
608       ll--;
609     newdat.print (p, ll);
610     newdat.print ("\n", 1);
611   }
612 
613   assert (data_state == NEWLINE || p < eom);
614 
615   switch (data_state) {
616   case MIDLINE:
617     break;
618   case CR:
619     if (p < eom)
620       newdat.print ("\r", 1);
621     data_state = MIDLINE;
622     break;
623   case NEWLINE:
624     if (p >= eom)
625       break;
626     else if (*p == '.') {
627       p++;
628       data_state = DOT;
629     }
630     else {
631       data_state = MIDLINE;
632       break;
633     }
634     /* cascade */
635   case DOT:
636     if (p >= eom)
637       break;
638     else if (*p == '\r') {
639       p++;
640       data_state = DOTCR;
641     }
642     else {
643       if (*p != '.')
644 	newdat.print (".", 1);
645       data_state = MIDLINE;
646       break;
647     }
648     /* cascade */
649   case DOTCR:
650     if (p < eom) {
651       newdat.print (".\r", 2);
652       data_state = MIDLINE;
653     }
654     break;
655   }
656 
657   ll = eom - p;
658   if (ll > 0 && data_state == MIDLINE && p[ll - 1] == '\r') {
659     ll--;
660     data_state = CR;
661   }
662   newdat.print ((char *) p, ll);
663 
664  done:
665   data_msgsize += newdat.resid ();
666   if (!data_err && data_msgsize > opt->max_msgsize) {
667     delete data_q;
668     data_q = New enqmsg_dummy;
669     data_err = "552 Message too large\r\n";
670   }
671   if (end)
672     aio->unread (eom - (np + 1));
673   if (data_err)
674     data_2 (end, NULL);
675   else
676     data_q->writev (&newdat, wrap (this, &smtpd::data_2, end));
677 }
678 
679 void
data_2(bool end,str err)680 smtpd::data_2 (bool end, str err)
681 {
682   int fd;
683   struct passwd *pw;
684 
685   if (err) {
686     assert (err[0] == '4' || err[0] == '5');
687     data_err = err;
688   }
689   if (!end)
690     aio->readany (wrap (this, &smtpd::data_1));
691   else if (data_err)
692     data_4 (NULL);
693   else if (body_cmd
694 	   && (pw = body_user ? getpwnam (body_user) : opt->av_user)
695 	   && (fd = data_q->getfd ()) >= 0) {
696     avcount *avc = avcount::get (pw->pw_uid);
697     if (!avc->acquire ()) {
698       avc->waiters.push_back (wrap (this, &smtpd::data_2, true, str (NULL)));
699       return;
700     }
701 
702     vec<str> senv;
703     envinit (&senv, pw);
704     senv.push_back (strbuf ("DATA_BYTES=%ld", long (data_msgsize)));
705 
706     vec<const char *> env;
707     env.reserve (senv.size () + 1);
708     for (const str *sp = senv.base (); sp < senv.lim (); sp++)
709       env.push_back (sp->cstr ());
710     env.push_back (NULL);
711 
712     vec<const char *> av;
713     av.push_back ("/bin/sh");
714     av.push_back ("-c");
715     av.push_back (body_cmd);
716     av.push_back (NULL);
717 
718     rpstate *rp = runprog (av[0], av.base (), fd, false,
719 			   wrap (this, &smtpd::data_3, avc),
720 			   wrap (become_user, pw, body_user), env.base ());
721     runprog_timeout (rp, opt->avenger_timeout);
722   }
723   else
724     data_q->commit (wrap (this, &smtpd::data_4));
725 }
726 
727 void
data_3(avcount * avc,ref<progout> po)728 smtpd::data_3 (avcount *avc, ref<progout> po)
729 {
730   avc->release ();
731 
732   if (!po->status) {
733     data_q->commit (wrap (this, &smtpd::data_4));
734     return;
735   }
736 
737   if (!WIFEXITED (po->status)) {
738     data_4 (strbuf () << "451 body filter exited with "
739 	    << exitstr (po->status) << "\r\n");
740     return;
741   }
742 
743   int code = 451;
744   switch (WEXITSTATUS (po->status)) {
745   case 99:
746     data_4 (NULL);
747     return;
748   case 64:
749   case 65:
750   case 70:
751   case 76:
752   case 77:
753   case 78:
754   case 100:
755   case 112:
756     code = 554;
757     break;
758   }
759   if (po->output.empty ())
760     data_4 (strbuf ("%03d message contents rejected\r\n", code));
761   else
762     data_4 (po->response (code));
763 }
764 
765 void
data_4(str err)766 smtpd::data_4 (str err)
767 {
768   str resp;
769   if (err && (err[0] == '2' || err[0] == '4' || err[0] == '5'))
770     resp = err;
771   else if (data_err)
772     resp = data_err;
773   else
774     resp = okstr;
775 
776   delete data_q;
777   data_q = NULL;
778   num_indata--;
779   aio->settimeout (opt->smtp_timeout);
780   reset ();
781   respond (resp);
782 }
783 
smtp_dispatch(const char * name,void (smtpd::* fn)(str,str))784 smtp_dispatch::smtp_dispatch (const char *name, void (smtpd::*fn) (str, str))
785   : cmd_name (name), cmd_fn (fn)
786 {
787   //warn ("inserting %s\n", name);
788   dispatch_tab.insert (this);
789 }
790 
791 static void
relaunch(int __XXX_gcc_2_95_3_bug,int myfd,str name,in_addr addr,ptr<hostent> h,int err)792 relaunch (int __XXX_gcc_2_95_3_bug,
793 	  int myfd, str name, in_addr addr, ptr<hostent> h, int err)
794 {
795   static rxx userrx ("^((.*)@)?[^@]+$");
796   if (userrx.match (name))
797     name = userrx[2];
798   else
799     name = NULL;
800 
801   strbuf sb ("[test");
802   if (name)
803     sb << "=" << name;
804   sb << "]@";
805   if (h)
806     sb << h->h_name;
807   else
808     sb << inet_ntoa (addr);
809 
810   name = sb;
811 
812   sockaddr_in sin;
813   bzero (&sin, sizeof (sin));
814   sin.sin_family = AF_INET;
815   sin.sin_port = htons (0);
816   sin.sin_addr = addr;
817 
818   newcon *nc = New newcon (myfd, sin);
819   nc->name = name;
820   nc->h = h;
821   nc->init ();
822 }
823 
824 void
getcmd(str line,int err)825 smtpd::getcmd (str line, int err)
826 {
827   cmdwait = false;
828 
829   if (!dispatch_tab.size ())
830     dispatch_tab_init ();
831 
832   if (!line) {
833     if (err == ETIMEDOUT)
834       aio << "421 timeout\r\n";
835     delete this;
836     return;
837   }
838 
839   if (terminated) {
840     aio << "421 shutdown\r\n";
841     delete this;
842     return;
843   }
844 
845   if (ii)
846     if (str err = ii->status ()) {
847       aio << err;
848       delete this;
849       return;
850     }
851 
852   if (!cmdarg.match (line)) {
853     respond ("502 command not implemented\r\n");
854     return;
855   }
856 
857   str cmd = mytolower (cmdarg[1]);
858   line = cmdarg [3];
859   if (!line)
860     line = "";
861 
862   if (!helohost && aio->getrbufbytes ())
863     pipelining = true;
864 
865   if (cmd == "helo" || cmd == "ehlo") {
866     /* We wait until here to call netpath, so as to avoid sending out
867      * a bunch of UDP packets in response to a forged SYN packet.  (If
868      * we get as far as receiving the HELO command, at least the
869      * client has acked our ISN.) */
870     if (!helohost && ii)
871       ii->do_netpath (myipaddr);
872     if (line && !strchr (line, ' ')) {
873       helohost = line;
874       strbuf sb;
875       sb << "250-" << opt->hostname << "\r\n"
876 	 << helo_auth ()
877 #ifdef STARTTLS
878 	 << helo_starttls ()
879 #endif /* STARTTLS */
880 	 << "250 PIPELINING\r\n";
881       respond (sb);
882     }
883     else
884       respond ("501 syntax error\r\n");
885   }
886   else if (cmd == "post") {
887     post = true;
888     respond ("502 command not implemented\r\n");
889   }
890   else if (cmd == "quit") {
891     aio << "221 " << opt->hostname << "\r\n";
892     delete this;
893   }
894   else if (cmd == "noop")
895     respond (okstr);
896 #ifdef STARTTLS
897   else if (cmd == "starttls")
898     cmd_starttls (cmd, line);
899 #endif /* STARTTLS */
900   else if (trust >= TRUST_LOCAL && cmd == "stat") {
901     quota_dump (aiosout (aio));
902     cmdwait = true;
903     aio->readline (wrap (this, &smtpd::getcmd));
904   }
905   else if (trust >= TRUST_LOCAL && cmd == "addr") {
906     in_addr a;
907     if (!line || inet_aton (line, &a) != 1)
908       respond ("501 syntax error\r\n");
909     else {
910       str nn (name);
911       int myfd = dup (aio->fdno ());
912       delete this;
913       dns_hostbyaddr (a, wrap (relaunch, myfd, myfd, nn, a));
914       return;
915     }
916   }
917   else {
918     const smtp_dispatch *dp = dispatch_tab[cmd];
919     if (!dp) {
920       respond ("502 command not implemented\r\n");
921       //dp = dispatch_tab.first ();
922       //warn ("%s %p %s\n", cmd.cstr (), dp, dp ? dp->cmd_name : "NULL");
923     }
924     else if (!helohost)
925       respond ("503 hello?\r\n");
926     else
927       (this->*(dp->cmd_fn)) (cmd, line);
928   }
929 }
930 
931 void
respond(str resp,bool counterr)932 smtpd::respond (str resp, bool counterr)
933 {
934   if (counterr && (resp[0] == '4' || resp[0] == '5') && ii)
935     ii->error ();
936   aio << resp;
937   if (terminated) {
938     aio << "421 shutdown\r\n";
939     delete this;
940   }
941   else {
942     cmdwait = true;
943     aio->readline (wrap (this, &smtpd::getcmd));
944   }
945 }
946 
smtpd(ipinfo * ii,int fd,const sockaddr_in & sin,str n,str fp,trust_level t,ptr<rbl_status> rs,ptr<hostent> h,str dnserr)947 smtpd::smtpd (ipinfo *ii, int fd, const sockaddr_in &sin,
948 	      str n, str fp, trust_level t,
949 	      ptr<rbl_status> rs, ptr<hostent> h, str dnserr)
950   : ii (ii), aio (AIOS::alloc (fd)), name (n),
951     synfp (fp), osguess (synos_guess (fp)), pipelining (false),
952     colonspace (false), post (false), encrypted (false), trust (t),
953     rblcon (rs), dns_error (dnserr), msgid (msgid_gen ()),
954     mask_spf (false), spfr (SPF_PASS),
955 #ifdef SASL
956     sasl (NULL),
957 #endif /* SASL */
958     body_set (false), data_q (NULL), cmdwait (false), ptr_cache (h)
959 {
960   smtplist.insert_head (this);
961   num_smtpd++;
962 
963   ipaddr = sin.sin_addr;
964   tcpport = ntohs (sin.sin_port);
965 
966   sockaddr_in lsin;
967   socklen_t sinlen = sizeof (lsin);
968   bzero (&lsin, sizeof (lsin));
969   getsockname (fd, (sockaddr *) &lsin, &sinlen);
970   myipaddr = lsin.sin_addr;
971   mytcpport = ntohs (lsin.sin_port);
972 
973   if (opt->debug_smtpd) {
974     aio->setdebug (strbuf ("%s (%d)", name.cstr (), fd));
975 #if 0
976     if (synfp)
977       warnx ("%s (%d) === SYN fingerprint %s\n", name.cstr (), fd,
978 	     synfp.cstr ());
979 #endif
980   }
981   aio->settimeout (opt->smtp_timeout);
982 
983   str trusted;
984   switch (trust) {
985   case TRUST_MAIL:
986     trusted = " any sender ok";
987     break;
988   case TRUST_RCPT:
989   case TRUST_AUTH:
990     trusted = " mail relaying ok";
991     break;
992   case TRUST_LOCAL:
993     trusted = " trusted local client";
994     break;
995   default:
996     trusted = "";
997     break;
998   }
999   respond (strbuf () << "220 " << opt->hostname << " ESMTP"
1000 	   << trusted << "\r\n");
1001 }
1002 
~smtpd()1003 smtpd::~smtpd ()
1004 {
1005   assert (!data_q);
1006   num_smtpd--;
1007   if (ii)
1008     ii->delcon ();
1009   smtplist.remove (this);
1010 #ifdef SASL
1011   if (sasl)
1012     sasl_dispose (&sasl);
1013 #endif /* SASL */
1014   toggle_listen ();
1015 }
1016 
1017 inline bool
null_str_eq(const str & s1,const str & s2)1018 null_str_eq (const str &s1, const str &s2)
1019 {
1020   return s1 && s2 ? s1 == s2 : !s1 && !s2;
1021 }
1022 str
bodycheck(str user,str cmd,str defresp)1023 smtpd::bodycheck (str user, str cmd, str defresp)
1024 {
1025   if (!body_set) {
1026     body_set = true;
1027     body_user = user;
1028     body_cmd = cmd;
1029     return defresp;
1030   }
1031   if (null_str_eq (body_user, user) && null_str_eq (body_cmd, cmd))
1032     return defresp;
1033   return "452 send a separate copy of the message to this user\r\n";
1034 }
1035 
1036 inline void
preserve(vec<str> * e,const char * var)1037 preserve (vec<str> *e, const char *var)
1038 {
1039   if (!opt->envb[var])
1040     if (const char *p = getenv (var))
1041       e->push_back (strbuf ("%s=%s", var, p));
1042 }
1043 void
1044 smtpd::envinit (vec<str> *envp, struct passwd *pw = NULL) const
1045 {
1046   preserve (envp, "PWD");
1047   preserve (envp, "PATH");
1048   preserve (envp, "TZ");
1049   preserve (envp, "TMPDIR");
1050   preserve (envp, "DMALLOC_OPTIONS");
1051   preserve (envp, "STKTRACE");
1052 
1053   for (const str *sp = opt->env.base (); sp < opt->env.lim (); sp++)
1054     envp->push_back (*sp);
1055   envp->push_back (strbuf () << "HOST=" << opt->hostname);
1056   envp->push_back (strbuf () << "ETCDIR=" << opt->etcdir);
1057   if (opt->separator)
1058     envp->push_back (strbuf ("SEPARATOR=%c", opt->separator));
1059   if (mytcpport) {
1060     envp->push_back (strbuf ("MYIP=%s", inet_ntoa (myipaddr)));
1061     envp->push_back (strbuf ("MYPORT=%d", mytcpport));
1062   }
1063 
1064   if (mail_error)
1065     envp->push_back (strbuf () << "MAIL_ERROR=" << mail_error);
1066   if (auth_user)
1067     envp->push_back (strbuf () << "AUTH_USER=" << auth_user);
1068 #ifdef STARTTLS
1069   env_starttls (envp);
1070 #endif /* STARTTLS */
1071   envp->push_back (strbuf () << "MSGID=" << msgid);
1072   envp->push_back (strbuf () << "CLIENT=" << name);
1073   envp->push_back (strbuf ("CLIENT_IP=%s", inet_ntoa (ipaddr)));
1074   {
1075     const u_char *c = reinterpret_cast<const u_char *> (&ipaddr);
1076     envp->push_back (strbuf ("CLIENT_REVIP=%d.%d.%d.%d",
1077 			    c[3], c[2], c[1], c[0]));
1078   }
1079   if (dns_error)
1080     envp->push_back (strbuf ("CLIENT_DNSFAIL=") << dns_error);
1081   if (tcpport)
1082     envp->push_back (strbuf ("CLIENT_PORT=%d", tcpport));
1083   if (ptr_cache)
1084     envp->push_back (strbuf ("CLIENT_NAME=%s", ptr_cache->h_name));
1085   if (synfp)
1086     envp->push_back (strbuf () << "CLIENT_SYNFP=" << synfp);
1087   if (osguess)
1088     envp->push_back (strbuf () << "CLIENT_SYNOS=" << osguess);
1089   envp->push_back (strbuf () << "CLIENT_HELO=" << helohost);
1090   if (pipelining)
1091     envp->push_back ("CLIENT_PIPELINING=1");
1092   if (colonspace)
1093     envp->push_back ("CLIENT_COLONSPACE=1");
1094   if (post)
1095     envp->push_back ("CLIENT_POST=1");
1096   if (ii && ii->nhops > 0)
1097     envp->push_back (strbuf ("CLIENT_NETHOPS=%d", ii->nhops));
1098   if (ii && ii->netpath)
1099     envp->push_back (strbuf () << "CLIENT_NETPATH=" << ii->netpath);
1100   envp->push_back (strbuf () << "SENDER=" << fromaddr);
1101   if (str s = extract_domain (fromaddr)) {
1102     envp->push_back (strbuf () << "SENDER_HOST=" << mytolower (s));
1103     if ((s = extract_local (fromaddr)))
1104       envp->push_back (strbuf () << "SENDER_LOCAL=" << mytolower (s));
1105   }
1106   if (mxl) {
1107     strbuf sb ("SENDER_MXES=%s", mxl->m_mxes[0].name);
1108     for (u_int i = 1; i < mxl->m_nmx; i++)
1109       sb.fmt (" %s", mxl->m_mxes[i].name);
1110     envp->push_back (sb);
1111   }
1112   if (bounce_res)
1113     envp->push_back (strbuf () << "SENDER_BOUNCERES=" << bounce_res);
1114   envp->push_back (strbuf ("SPF=%s", spf_print (spfr)));
1115   envp->push_back (strbuf ("SPF0=%s", spf_print (spfr)));
1116   envp->push_back (strbuf ("SPF1=%s", spf1_print (spfr)));
1117   if (spf_expl)
1118     envp->push_back (strbuf () << "SPF_EXPL=" << spf_expl);
1119   if (rblenv)
1120     for (rbl_status::result *rp = rblenv->results.base ();
1121 	 rp < rblenv->results.lim (); rp++)
1122       envp->push_back (rp->tostr (true));
1123   envp->push_back (strbuf () << "UFLINE=From "
1124 		   << (fromaddr.len () ? fromaddr.cstr () : "MAILER-DAEMON")
1125 		   << " " << datestr (false));
1126 
1127   if (pw) {
1128     if (pw->pw_dir)
1129       envp->push_back (strbuf ("HOME=%s", pw->pw_dir));
1130     if (pw->pw_shell)
1131       envp->push_back (strbuf ("SHELL=%s", pw->pw_shell));
1132     envp->push_back (strbuf ("USER=%s", pw->pw_name));
1133     envp->push_back (strbuf ("LOGNAME=%s", pw->pw_name));
1134   }
1135 }
1136 
1137 void
maybe_shutdown()1138 smtpd::maybe_shutdown ()
1139 {
1140   if (!cmdwait)
1141     return;
1142   aio->readcancel ();
1143   aio << "421 shutdown\r\n";
1144   delete this;
1145 }
1146