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