1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2007-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include "libmda.h"
18 #include <mailutils/server.h>
19 #include <mailutils/daemon.h>
20 #include <tcpwrap.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <netdb.h>
24 #include <sys/un.h>
25 
26 static const char *program_version = "lmtpd (" PACKAGE_STRING ")";
27 
28 static mu_m_server_t server;
29 static int reuse_lmtp_address = 1;
30 static int mda_transcript;
31 static mu_list_t lmtp_groups;
32 
33 static int
cb2_group(const char * gname,void * data)34 cb2_group (const char *gname, void *data)
35 {
36   mu_list_t list = data;
37   struct group *group;
38 
39   group = getgrnam (gname);
40   if (!group)
41     mu_error (_("unknown group: %s"), gname);
42   else
43     mu_list_append (list, (void*) (intptr_t) group->gr_gid);
44   return 0;
45 }
46 
47 static int
cb_group(void * data,mu_config_value_t * arg)48 cb_group (void *data, mu_config_value_t *arg)
49 {
50   mu_list_t *plist = data;
51 
52   if (!*plist)
53     mu_list_create (plist);
54   return mu_cfg_string_value_cb (arg, cb2_group, *plist);
55 }
56 
57 static int
cb_listen(void * data,mu_config_value_t * val)58 cb_listen (void *data, mu_config_value_t *val)
59 {
60   struct mu_sockaddr *s;
61 
62   if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
63     return 1;
64   if (mu_m_server_parse_url (server, val->v.string, &s))
65     return 1;
66   mu_m_server_listen (server, s, MU_IP_TCP);
67   return 0;
68 }
69 
70 static void
set_foreground(struct mu_parseopt * po,struct mu_option * opt,char const * arg)71 set_foreground (struct mu_parseopt *po, struct mu_option *opt,
72 		char const *arg)
73 {
74   mu_m_server_set_foreground (server, 1);
75 }
76 
77 static void
set_stderr(struct mu_parseopt * po,struct mu_option * opt,char const * arg)78 set_stderr (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
79 {
80   mu_log_syslog = 0;
81 }
82 
83 static void
set_inetd_mode(struct mu_parseopt * po,struct mu_option * opt,char const * arg)84 set_inetd_mode (struct mu_parseopt *po, struct mu_option *opt,
85 		char const *arg)
86 {
87   mu_m_server_set_mode (server, MODE_INTERACTIVE);
88 }
89 
90 static void
set_daemon_mode(struct mu_parseopt * po,struct mu_option * opt,char const * arg)91 set_daemon_mode (struct mu_parseopt *po, struct mu_option *opt,
92 		 char const *arg)
93 {
94   mu_m_server_set_mode (server, MODE_DAEMON);
95   if (arg)
96     {
97       size_t max_children;
98       char *errmsg;
99       int rc = mu_str_to_c (arg, mu_c_size, &max_children, &errmsg);
100       if (rc)
101 	{
102 	  mu_parseopt_error (po, _("%s: bad argument"), arg);
103 	  exit (po->po_exit_error);
104 	}
105       mu_m_server_set_max_children (server, max_children);
106     }
107 }
108 
109 static struct mu_option lmtpd_options[] = {
110   MU_OPTION_GROUP (N_("General options")),
111   { "foreground",  0, NULL, MU_OPTION_DEFAULT,
112     N_("remain in foreground"),
113     mu_c_bool, NULL, set_foreground },
114   { "inetd",  'i', NULL, MU_OPTION_DEFAULT,
115     N_("run in inetd mode"),
116     mu_c_bool, NULL, set_inetd_mode },
117   { "daemon", 'd', N_("NUMBER"), MU_OPTION_ARG_OPTIONAL,
118     N_("runs in daemon mode with a maximum of NUMBER children"),
119     mu_c_string, NULL, set_daemon_mode },
120   { "stderr", 0, NULL, MU_OPTION_DEFAULT,
121     N_("log to standard error"),
122     mu_c_string, NULL, set_stderr },
123   { "transcript", 0, NULL, MU_OPTION_DEFAULT,
124     N_("enable session transcript"),
125     mu_c_bool, &mda_transcript },
126   MU_OPTION_END
127 }, *options[] = { lmtpd_options, NULL };
128 
129 struct mu_cfg_param lmtp_cfg_param[] = {
130   { "group", mu_cfg_callback, &lmtp_groups, 0, cb_group,
131     N_("In LMTP mode, retain these supplementary groups."),
132     N_("groups: list of string") },
133   { "listen", mu_cfg_callback, NULL, 0, cb_listen,
134     N_("In LMTP mode, listen on the given URL.  Valid URLs are:\n"
135        "   tcp://<address: string>:<port: number> (note that port is "
136        "mandatory)\n"
137        "   file://<socket-file-name>\n"
138        "or socket://<socket-file-name>"),
139     N_("url: string") },
140   { "reuse-address", mu_c_bool, &reuse_lmtp_address, 0, NULL,
141     N_("Reuse existing address (LMTP mode).  Default is \"yes\".") },
142   { "filter", mu_cfg_section, NULL, 0, NULL,
143     N_("Add a message filter") },
144   { ".server", mu_cfg_section, NULL, 0, NULL,
145     N_("LMTP server configuration.") },
146   TCP_WRAPPERS_CONFIG
147   { NULL }
148 };
149 
150 static char *capa[] = {
151   "auth",
152   "debug",
153   "logging",
154   "mailbox",
155   "locking",
156   "mailer",
157   "sieve",
158   "deliver",
159   "forward",
160   "quota",
161   "script",
162   NULL
163 };
164 
165 static struct mu_cli_setup cli = {
166   options,
167   lmtp_cfg_param,
168   N_("GNU lmtpd -- local mail transfer protocol daemon."),
169 };
170 
171 static int lmtp_connection (int fd, struct sockaddr *sa, int salen,
172 			    struct mu_srv_config *pconf,
173 			    void *data);
174 static int lmtp_server (void);
175 
176 
177 int
main(int argc,char ** argv)178 main (int argc, char **argv)
179 {
180   umask (0077);
181 
182   /* Native Language Support */
183   MU_APP_INIT_NLS ();
184 
185   /* Default locker settings */
186   mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
187   mu_locker_defaults.retry_sleep = 1;
188   mu_locker_defaults.retry_count = 300;
189 
190   /* Register needed modules */
191   MU_AUTH_REGISTER_ALL_MODULES ();
192 
193   /* Register all supported mailbox and mailer formats */
194   mu_register_all_formats ();
195   mu_registrar_record (mu_smtp_record);
196 
197   mda_filter_cfg_init ();
198   mu_tcpwrapper_cfg_init ();
199   mu_acl_cfg_init ();
200   mda_cli_capa_init ();
201 
202   mu_m_server_create (&server, program_version);
203   mu_m_server_set_conn (server, lmtp_connection);
204   mu_m_server_set_prefork (server, mu_tcp_wrapper_prefork);
205   mu_m_server_set_mode (server, MODE_INTERACTIVE);
206   mu_m_server_set_max_children (server, 20);
207   mu_m_server_set_timeout (server, 600);
208   mu_m_server_cfg_init (server, NULL);
209 
210   /* Parse command line */
211   mu_cli (argc, argv, &cli, capa, server, &argc, &argv);
212 
213   mu_stdstream_strerr_setup (mu_log_syslog ?
214 			     MU_STRERR_SYSLOG : MU_STRERR_STDERR);
215 
216   if (argc)
217     {
218       mu_error (_("too many arguments"));
219       return EX_USAGE;
220     }
221 
222   return lmtp_server ();
223 }
224 
225 static mu_stream_t
lmtp_transcript(mu_stream_t iostream)226 lmtp_transcript (mu_stream_t iostream)
227 {
228   int rc;
229   mu_stream_t dstr, xstr;
230 
231   rc = mu_dbgstream_create (&dstr, MU_DIAG_DEBUG);
232   if (rc)
233     mu_error (_("cannot create debug stream; transcript disabled: %s"),
234 	      mu_strerror (rc));
235   else
236     {
237       rc = mu_xscript_stream_create (&xstr, iostream, dstr, NULL);
238       if (rc)
239 	mu_error (_("cannot create transcript stream: %s"),
240 		  mu_strerror (rc));
241       else
242 	{
243 	  /* FIXME: Would do a mu_stream_unref (iostream) here,
244 	     however mu_xscript_stream_create *may* steal the reference.
245 	     This should be fixed in mu_xscript_stream_create. */
246 	  iostream = xstr;
247 	}
248     }
249   return iostream;
250 }
251 
252 static void
lmtp_reply(mu_stream_t iostr,char * code,char * enh,char * fmt,...)253 lmtp_reply (mu_stream_t iostr, char *code, char *enh, char *fmt, ...)
254 {
255   va_list ap;
256   char *str = NULL;
257   size_t size = 0;
258 
259   va_start (ap, fmt);
260   mu_vasnprintf (&str, &size, fmt, ap);
261   va_end (ap);
262 
263   if (!str)
264     {
265       mu_error (_("not enough memory"));
266       exit (EX_TEMPFAIL);
267     }
268 
269   while (*str)
270     {
271       char *end = strchr (str, '\n');
272 
273       if (end)
274 	{
275 	  size_t len = end - str;
276 	  mu_stream_printf (iostr, "%s-", code);
277 	  if (enh)
278 	    mu_stream_printf (iostr, "%s ", enh);
279 	  mu_stream_printf (iostr, "%.*s\r\n", (int) len, str);
280 	  for (str = end; *str && *str == '\n'; str++);
281 	}
282       else
283 	{
284 	  mu_stream_printf (iostr, "%s ", code);
285 	  if (enh)
286 	    mu_stream_printf (iostr, "%s ", enh);
287 	  mu_stream_printf (iostr, "%s\r\n", str);
288 	  str += strlen (str);
289 	}
290     }
291 }
292 
293 enum lmtp_state
294   {
295     state_none,
296 
297     state_init,
298     state_lhlo,
299     state_mail,
300     state_rcpt,
301     state_data,
302     state_quit,
303     state_dot,
304 
305     state_end
306   };
307 
308 #define NSTATE ((int) state_end + 1)
309 
310 enum lmtp_command
311   {
312     cmd_unknown,
313     cmd_lhlo,
314     cmd_mail,
315     cmd_rcpt,
316     cmd_data,
317     cmd_quit,
318     cmd_rset,
319     cmd_help,
320     cmd_dot
321   };
322 
323 #define NCMD ((int)cmd_dot + 1)
324 
325 #define SNO state_none
326 #define SIN state_init
327 #define SHL state_lhlo
328 #define SML state_mail
329 #define SRC state_rcpt
330 #define SDA state_data
331 #define SQT state_quit
332 #define SDT state_dot
333 #define SEN state_end
334 
335 static int transtab[NCMD][NSTATE] = {
336 /* state_     SNO  SIN  SHL  SML  SRC  SDA  SQT  SDT  SEN */
337 /* unkn */  { SNO, SNO, SNO, SNO, SNO, SNO, SNO, SNO, SEN },
338 /* lhlo */  { SNO, SHL, SNO, SNO, SNO, SNO, SNO, SNO, SNO },
339 /* mail */  { SNO, SNO, SML, SNO, SNO, SNO, SNO, SNO, SNO },
340 /* rcpt */  { SNO, SNO, SNO, SRC, SRC, SNO, SNO, SNO, SNO },
341 /* data */  { SNO, SNO, SNO, SNO, SDA, SNO, SNO, SNO, SNO },
342 /* quit */  { SNO, SEN, SEN, SEN, SEN, SEN, SEN, SEN, SEN },
343 /* rset */  { SNO, SIN, SIN, SIN, SIN, SIN, SIN, SIN, SNO },
344 /* help */  { SNO, SIN, SHL, SML, SRC, SDT, SQT, SDT, SEN },
345 /* dot  */  { SNO, SNO, SNO, SNO, SNO, SQT, SNO, SNO, SNO },
346 };
347 
348 
349 /* Delivery data */
350 static char *lhlo_domain;     /* Sender domain */
351 static char *mail_from;       /* Sender address */
352 static mu_list_t rcpt_list;   /* Recipient addresses */
353 static mu_message_t mesg;     /* Collected message */
354 
355 
356 static int
cfun_unknown(mu_stream_t iostr,char * arg)357 cfun_unknown (mu_stream_t iostr, char *arg)
358 {
359   lmtp_reply (iostr, "500", "5.5.1", "Command unrecognized");
360   return 0;
361 }
362 
363 
364 static void
add_default_domain(char * str,int len,char ** pret)365 add_default_domain (char *str, int len, char **pret)
366 {
367   *pret = malloc (len + 1 + strlen (lhlo_domain) + 1);
368   if (!*pret)
369     {
370       mu_error (_("not enough memory"));
371       exit (EX_SOFTWARE);
372     }
373   memcpy (*pret, str, len);
374   (*pret)[len] = '@';
375   strcpy (*pret + len + 1, lhlo_domain);
376 }
377 
378 #define MAILER_DAEMON "MAILER-DAEMON"
379 
380 static int
check_address(char * arg,int with_domain,char ** pret)381 check_address (char *arg, int with_domain, char **pret)
382 {
383   if (strchr (arg, '@') == 0)
384     {
385       char *addr = NULL;
386       size_t addrlen = 0;
387 
388       if (*arg == '<')
389 	{
390 	  size_t len = strlen (arg);
391 	  if (arg[len - 1] == '>')
392 	    {
393 	      if (len == 2) /* null address */
394 		{
395 		  if (!with_domain)
396 		    /* Null address is only legal in mail from */
397 		    return 1;
398 		  addr = MAILER_DAEMON;
399 		  addrlen = sizeof MAILER_DAEMON - 1;
400 		}
401 	      else
402 		{
403 		  addr = arg + 1;
404 		  addrlen = len - 2;
405 		}
406 	    }
407 	  else
408 	    return 1;
409 	}
410       else
411 	{
412 	  addr = arg;
413 	  addrlen = strlen (arg);
414 	}
415 
416       if (with_domain)
417 	add_default_domain (addr, addrlen, pret);
418       else
419 	{
420 	  *pret = malloc (addrlen + 1);
421 	  memcpy (*pret, addr, addrlen);
422 	  (*pret)[addrlen] = 0;
423 	}
424     }
425   else
426     {
427       mu_address_t addr;
428       char *s;
429       int rc = mu_address_create (&addr, arg);
430       if (rc)
431 	return 1;
432       if (with_domain)
433 	rc = mu_address_aget_email (addr, 1, &s);
434       else
435 	rc = mu_address_aget_local_part (addr, 1, &s);
436       mu_address_destroy (&addr);
437       if (rc || !s)
438 	return 1;
439       *pret = s;
440     }
441   return 0;
442 }
443 
444 static int
cfun_mail_from(mu_stream_t iostr,char * arg)445 cfun_mail_from (mu_stream_t iostr, char *arg)
446 {
447   if (*arg == 0)
448     {
449       lmtp_reply (iostr, "501", "5.5.2", "Syntax error");
450       return 1;
451     }
452 
453   if (check_address (arg, 1, &mail_from))
454     {
455       lmtp_reply (iostr, "553", "5.1.8", "Address format error");
456       return 1;
457     }
458   lmtp_reply (iostr, "250", "2.1.0", "Go ahead");
459   return 0;
460 }
461 
462 static int
cfun_rcpt_to(mu_stream_t iostr,char * arg)463 cfun_rcpt_to (mu_stream_t iostr, char *arg)
464 {
465   char *user;
466   struct mu_auth_data *auth;
467 
468   if (*arg == 0)
469     {
470       lmtp_reply (iostr, "501", "5.5.2", "Syntax error");
471       return 1;
472     }
473 
474   /* FIXME: Check if domain is OK */
475   if (check_address (arg, 0, &user))
476     {
477       lmtp_reply (iostr, "553", "5.1.8", "Address format error");
478       return 1;
479     }
480   auth = mu_get_auth_by_name (user);
481   if (!auth)
482     {
483       lmtp_reply (iostr, "550", "5.1.1", "User unknown");
484       free (user);
485       return 1;
486     }
487   mu_auth_data_free (auth);
488   if (!rcpt_list)
489     {
490       mu_list_create (&rcpt_list);
491       mu_list_set_destroy_item (rcpt_list, mu_list_free_item);
492     }
493   mu_list_append (rcpt_list, user);
494   lmtp_reply (iostr, "250", "2.1.5", "Go ahead");
495   return 0;
496 }
497 
498 static int
dot_temp_fail(void * item,void * cbdata)499 dot_temp_fail (void *item, void *cbdata)
500 {
501   char *name = item;
502   mu_stream_t iostr = cbdata;
503   lmtp_reply (iostr, "450", "4.1.0", "%s: temporary failure", name);
504   return 0;
505 }
506 
507 static int
dot_deliver(void * item,void * cbdata)508 dot_deliver (void *item, void *cbdata)
509 {
510   char *name = item;
511   mu_stream_t iostr = cbdata;
512   char *errp = NULL;
513 
514   switch (mda_deliver_to_user (mesg, name, &errp))
515     {
516     case 0:
517       lmtp_reply (iostr, "250", "2.0.0", "%s: delivered", name);
518       break;
519 
520     case EX_UNAVAILABLE:
521       if (errp)
522 	lmtp_reply (iostr, "553", "5.1.8", "%s", errp);
523       else
524 	lmtp_reply (iostr, "553", "5.1.8", "%s: delivery failed", name);
525       break;
526 
527     default:
528       if (errp)
529 	lmtp_reply (iostr, "450", "4.1.0", "%s", errp);
530       else
531 	lmtp_reply (iostr, "450", "4.1.0",
532 		    "%s: temporary failure, try again later",
533 		    name);
534       break;
535     }
536   free (errp);
537   return 0;
538 }
539 
540 static int
cfun_data(mu_stream_t iostr,char * arg)541 cfun_data (mu_stream_t iostr, char *arg)
542 {
543   int rc;
544   mu_stream_t flt, tempstr;
545   time_t t;
546   struct tm *tm;
547   int xlev = MU_XSCRIPT_PAYLOAD, xlev_switch = 0;
548 
549   if (*arg)
550     {
551       lmtp_reply (iostr, "501", "5.5.2", "Syntax error");
552       return 1;
553     }
554 
555   rc = mu_filter_create (&flt, iostr, "CRLFDOT", MU_FILTER_DECODE,
556 			 MU_STREAM_READ|MU_STREAM_WRTHRU);
557   if (rc)
558     {
559       mda_error (_("unable to open filter: %s"),
560 		    mu_strerror (rc));
561       lmtp_reply (iostr, "450", "4.1.0", "Temporary failure, try again later");
562       return 1;
563     }
564 
565   rc = mu_temp_stream_create (&tempstr, 0);
566   if (rc)
567     {
568       mda_error (_("unable to open temporary stream: %s"), mu_strerror (rc));
569       mu_stream_destroy (&flt);
570       return 1;
571     }
572 
573   /* Write out envelope */
574   time (&t);
575   tm = gmtime (&t);
576   rc = mu_stream_printf (tempstr, "From %s ", mail_from);
577   if (rc == 0)
578     rc = mu_c_streamftime (tempstr, "%c%n", tm, NULL);
579   if (rc)
580     {
581       mda_error (_("copy error: %s"), mu_strerror (rc));
582       mu_stream_destroy (&flt);
583       mu_stream_destroy (&tempstr);
584       mu_list_foreach (rcpt_list, dot_temp_fail, iostr);
585     }
586 
587   lmtp_reply (iostr, "354", NULL, "Go ahead");
588 
589   if (mu_stream_ioctl (iostr, MU_IOCTL_XSCRIPTSTREAM,
590                        MU_IOCTL_XSCRIPTSTREAM_LEVEL, &xlev) == 0)
591     xlev_switch = 1;
592   rc = mu_stream_copy (tempstr, flt, 0, NULL);
593   mu_stream_destroy (&flt);
594   if (xlev_switch)
595     mu_stream_ioctl (iostr, MU_IOCTL_XSCRIPTSTREAM,
596                      MU_IOCTL_XSCRIPTSTREAM_LEVEL, &xlev);
597   if (rc)
598     {
599       mda_error (_("copy error: %s"), mu_strerror (rc));
600       mu_list_foreach (rcpt_list, dot_temp_fail, iostr);
601     }
602 
603   rc = mu_stream_to_message (tempstr, &mesg);
604   mu_stream_unref (tempstr);
605   if (rc)
606     {
607       mda_error (_("error creating temporary message: %s"),
608 		    mu_strerror (rc));
609       mu_list_foreach (rcpt_list, dot_temp_fail, iostr);
610     }
611 
612   rc = mu_list_foreach (rcpt_list, dot_deliver, iostr);
613 
614   mu_message_destroy (&mesg, mu_message_get_owner (mesg));
615   if (rc)
616     mu_list_foreach (rcpt_list, dot_temp_fail, iostr);
617 
618   return 0;
619 }
620 
621 static int
cfun_rset(mu_stream_t iostr,char * arg)622 cfun_rset (mu_stream_t iostr, char *arg)
623 {
624   free (lhlo_domain);
625   free (mail_from);
626   mu_list_destroy (&rcpt_list);
627   mu_message_destroy (&mesg, mu_message_get_owner (mesg));
628   lmtp_reply (iostr, "250", "2.0.0", "OK, forgotten");
629   return 0;
630 }
631 
632 static char *capa_str = "ENHANCEDSTATUSCODES\n\
633 PIPELINING\n\
634 8BITMIME\n\
635 HELP";
636 
637 static int
cfun_lhlo(mu_stream_t iostr,char * arg)638 cfun_lhlo (mu_stream_t iostr, char *arg)
639 {
640   if (*arg == 0)
641     {
642       lmtp_reply (iostr, "501", "5.0.0", "Syntax error");
643       return 1;
644     }
645   lhlo_domain = strdup (arg);
646   if (!lhlo_domain)
647     {
648       lmtp_reply (iostr, "410", "4.0.0",
649                   "Local error; please try again later");
650       return 1;
651     }
652   lmtp_reply (iostr, "250", NULL, "Hello\n");
653   lmtp_reply (iostr, "250", NULL, capa_str);
654   return 0;
655 }
656 
657 static int
cfun_quit(mu_stream_t iostr,char * arg)658 cfun_quit (mu_stream_t iostr, char *arg)
659 {
660   lmtp_reply (iostr, "221", "2.0.0", "Bye");
661   return 0;
662 }
663 
664 static int
cfun_help(mu_stream_t iostr,char * arg)665 cfun_help (mu_stream_t iostr, char *arg)
666 {
667   lmtp_reply (iostr, "200", "2.0.0", "Man, help yourself");
668   return 0;
669 }
670 
671 static struct command_tab
672 {
673   char *cmd_verb;
674   int cmd_len;
675   enum lmtp_command cmd_code;
676   int (*cmd_fun) (mu_stream_t, char *);
677 } command_tab[] = {
678 #define S(s) #s, (sizeof #s - 1)
679   { S(lhlo), cmd_lhlo, cfun_lhlo },
680   { S(mail from:), cmd_mail, cfun_mail_from },
681   { S(rcpt to:), cmd_rcpt, cfun_rcpt_to },
682   { S(data), cmd_data, cfun_data },
683   { S(quit), cmd_quit, cfun_quit },
684   { S(rset), cmd_rset, cfun_rset },
685   { S(help), cmd_help, cfun_help },
686   { NULL, 0, cmd_unknown, cfun_unknown }
687 };
688 
689 static struct command_tab *
getcmd(char * buf,char ** sp)690 getcmd (char *buf, char **sp)
691 {
692   struct command_tab *cp;
693   size_t len = strlen (buf);
694   for (cp = command_tab; cp->cmd_verb; cp++)
695     {
696       if (cp->cmd_len <= len
697 	  && mu_c_strncasecmp (cp->cmd_verb, buf, cp->cmd_len) == 0)
698 	{
699 	  *sp = buf + cp->cmd_len;
700 	  return cp;
701 	}
702     }
703   return cp;
704 }
705 
706 static int
to_fgets(mu_stream_t iostr,char ** pbuf,size_t * psize,size_t * pnread,unsigned int timeout)707 to_fgets (mu_stream_t iostr, char **pbuf, size_t *psize, size_t *pnread,
708 	  unsigned int timeout)
709 {
710   int rc;
711 
712   alarm (timeout);
713   rc = mu_stream_getline (iostr, pbuf, psize, pnread);
714   alarm (0);
715   return rc;
716 }
717 
718 static int
lmtp_loop(mu_stream_t iostr,unsigned int timeout)719 lmtp_loop (mu_stream_t iostr, unsigned int timeout)
720 {
721   size_t size = 0, n;
722   char *buf = NULL;
723   enum lmtp_state state = state_init;
724 
725   lmtp_reply (iostr, "220", NULL, "At your service");
726   while (to_fgets (iostr, &buf, &size, &n, timeout) == 0 && n)
727     {
728       char *sp;
729       struct command_tab *cp = getcmd (buf, &sp);
730       enum lmtp_command cmd = cp->cmd_code;
731       enum lmtp_state next_state = transtab[cmd][state];
732 
733       mu_rtrim_class (sp, MU_CTYPE_ENDLN);
734 
735       if (next_state != state_none)
736 	{
737 	  if (cp->cmd_fun)
738 	    {
739 	      sp = mu_str_skip_class (sp, MU_CTYPE_SPACE);
740 	      if (cp->cmd_fun (iostr, sp))
741 		continue;
742 	    }
743 	  state = next_state;
744 	}
745       else
746 	lmtp_reply (iostr, "503", "5.0.0", "Syntax error");
747 
748       if (state == state_end)
749 	break;
750     }
751   return 0;
752 }
753 
754 typedef union
755 {
756   struct sockaddr sa;
757   struct sockaddr_in s_in;
758   struct sockaddr_un s_un;
759 } all_addr_t;
760 
761 static int
lmtp_connection(int fd,struct sockaddr * sa,int salen,struct mu_srv_config * pconf,void * data)762 lmtp_connection (int fd, struct sockaddr *sa, int salen,
763 		 struct mu_srv_config *pconf,
764 		 void *data)
765 {
766   mu_stream_t str;
767   int rc;
768 
769   rc = mu_fd_stream_create (&str, NULL, fd, MU_STREAM_RDWR);
770   if (rc)
771     {
772       mu_diag_funcall (MU_DIAG_ERROR, "mu_fd_stream_create", NULL, rc);
773       return rc;
774     }
775   mu_stream_set_buffer (str, mu_buffer_line, 0);
776 
777   if (pconf->transcript || mda_transcript)
778     str = lmtp_transcript (str);
779   lmtp_loop (str, pconf->timeout);
780   mu_stream_destroy (&str);
781   return 0;
782 }
783 
784 static int
lmtp_set_privs(void)785 lmtp_set_privs (void)
786 {
787   gid_t gid;
788 
789   if (lmtp_groups)
790     {
791       gid_t *gidset = NULL;
792       size_t size = 0;
793       size_t j = 0;
794       mu_iterator_t itr;
795       int rc;
796 
797       rc = mu_list_count (lmtp_groups, &size);
798       if (rc)
799 	{
800 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_list_count", NULL, rc);
801 	  return EX_UNAVAILABLE;
802 	}
803       if (size == 0)
804 	return 0; /* nothing to do */
805       gidset = calloc (size, sizeof (gidset[0]));
806       if (!gidset)
807 	{
808 	  mu_error (_("not enough memory"));
809 	  return EX_UNAVAILABLE;
810 	}
811       if (mu_list_get_iterator (lmtp_groups, &itr) == 0)
812 	{
813 	  for (mu_iterator_first (itr);
814 	       !mu_iterator_is_done (itr); mu_iterator_next (itr))
815 	    mu_iterator_current (itr,
816 				 (void **)(gidset + j++));
817 	  mu_iterator_destroy (&itr);
818 	}
819       gid = gidset[0];
820       rc = setgroups (j, gidset);
821       free (gidset);
822       if (rc)
823 	{
824 	  mu_diag_funcall (MU_DIAG_ERROR, "setgroups", NULL, errno);
825 	  return EX_UNAVAILABLE;
826 	}
827     }
828   else
829     {
830       struct group *gr = getgrnam ("mail");
831       if (gr == NULL)
832 	{
833 	  mu_diag_funcall (MU_DIAG_ERROR, "getgrnam", "mail", errno);
834 	  return EX_UNAVAILABLE;
835 	}
836       gid = gr->gr_gid;
837     }
838   if (setgid (gid) == -1)
839     {
840       mu_diag_funcall (MU_DIAG_ERROR, "setgid", "mail", errno);
841       return EX_UNAVAILABLE;
842     }
843   return 0;
844 }
845 
846 static int
lmtp_server(void)847 lmtp_server (void)
848 {
849   int rc = lmtp_set_privs ();
850 
851   if (rc)
852     return rc;
853 
854   if (mu_m_server_mode (server) == MODE_DAEMON)
855     {
856       mu_m_server_begin (server);
857       rc = mu_m_server_run (server);
858       if (rc)
859 	rc = EX_CONFIG;
860       mu_m_server_end (server);
861       mu_m_server_destroy (&server);
862     }
863   else
864     {
865       mu_stream_t str, istream, ostream;
866 
867       rc = mu_stdio_stream_create (&istream, MU_STDIN_FD, MU_STREAM_READ);
868       if (rc)
869 	{
870 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_stdio_stream_create",
871 			   "MU_STDIN_FD", rc);
872 	  return EX_UNAVAILABLE;
873 	}
874 
875       rc = mu_stdio_stream_create (&ostream, MU_STDOUT_FD, MU_STREAM_WRITE);
876       if (rc)
877 	{
878 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_stdio_stream_create",
879 			   "MU_STDOUT_FD", rc);
880 	  return 1;
881 	}
882 
883       rc = mu_iostream_create (&str, istream, ostream);
884       mu_stream_unref (istream);
885       mu_stream_unref (ostream);
886       if (rc)
887 	{
888 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_iostream_create", NULL, rc);
889 	  return 1;
890 	}
891 
892       if (mda_transcript)
893 	str = lmtp_transcript (str);
894 
895       rc = lmtp_loop (str, 0);
896       mu_stream_destroy (&str);
897     }
898   return rc;
899 }
900 
901 
902