1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-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 "pop3d.h"
18 #include "mailutils/pam.h"
19 #include "mailutils/cli.h"
20 #include "mailutils/pop3.h"
21 #include "mailutils/kwd.h"
22 #include "tcpwrap.h"
23 
24 mu_mailbox_t mbox;
25 int state;
26 char *username;
27 char *md5shared;
28 
29 mu_m_server_t server;
30 unsigned int idle_timeout;
31 int pop3d_transcript;
32 int debug_mode;
33 int pop3d_xlines;
34 char *apop_database_name = APOP_PASSFILE;
35 int apop_database_safety = MU_FILE_SAFETY_ALL;
36 uid_t apop_database_owner;
37 int apop_database_owner_set;
38 
39 int initial_state = AUTHORIZATION;
40 
41 /* Should all the messages be undeleted on startup */
42 int undelete_on_startup;
43 #ifdef ENABLE_LOGIN_DELAY
44 /* Minimum allowed delay between two successive logins */
45 time_t login_delay = 0;
46 char *login_stat_file = LOGIN_STAT_FILE;
47 #endif
48 
49 unsigned expire = EXPIRE_NEVER; /* Expire messages after this number of days */
50 int expire_on_exit = 0;         /* Delete expired messages on exit */
51 
52 const char *program_version = "pop3d (" PACKAGE_STRING ")";
53 
54 static void
set_foreground(struct mu_parseopt * po,struct mu_option * opt,char const * arg)55 set_foreground (struct mu_parseopt *po, struct mu_option *opt,
56 		char const *arg)
57 {
58   mu_m_server_set_foreground (server, 1);
59 }
60 
61 static void
set_inetd_mode(struct mu_parseopt * po,struct mu_option * opt,char const * arg)62 set_inetd_mode (struct mu_parseopt *po, struct mu_option *opt,
63 		char const *arg)
64 {
65   mu_m_server_set_mode (server, MODE_INTERACTIVE);
66 }
67 
68 static void
set_daemon_mode(struct mu_parseopt * po,struct mu_option * opt,char const * arg)69 set_daemon_mode (struct mu_parseopt *po, struct mu_option *opt,
70 		 char const *arg)
71 {
72   mu_m_server_set_mode (server, MODE_DAEMON);
73   if (arg)
74     {
75       size_t max_children;
76       char *errmsg;
77       int rc = mu_str_to_c (arg, mu_c_size, &max_children, &errmsg);
78       if (rc)
79 	{
80 	  mu_parseopt_error (po, _("%s: bad argument"), arg);
81 	  exit (po->po_exit_error);
82 	}
83       mu_m_server_set_max_children (server, max_children);
84     }
85 }
86 
87 static struct mu_option pop3d_options[] = {
88   { "foreground",  0, NULL, MU_OPTION_DEFAULT,
89     N_("remain in foreground"),
90     mu_c_bool, NULL, set_foreground },
91   { "inetd",  'i', NULL, MU_OPTION_DEFAULT,
92     N_("run in inetd mode"),
93     mu_c_bool, NULL, set_inetd_mode },
94   { "daemon", 'd', N_("NUMBER"), MU_OPTION_ARG_OPTIONAL,
95     N_("runs in daemon mode with a maximum of NUMBER children"),
96     mu_c_string, NULL, set_daemon_mode },
97   MU_OPTION_END
98 }, *options[] = { pop3d_options, NULL };
99 
100 static int
cb_bulletin_source(void * data,mu_config_value_t * val)101 cb_bulletin_source (void *data, mu_config_value_t *val)
102 {
103   if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
104     return 1;
105   set_bulletin_source (val->v.string); /* FIXME: Error reporting? */
106   return 0;
107 }
108 
109 static int
cb2_file_safety_checks(const char * name,void * data)110 cb2_file_safety_checks (const char *name, void *data)
111 {
112   if (mu_file_safety_compose (data, name, MU_FILE_SAFETY_ALL))
113     mu_error (_("unknown keyword: %s"), name);
114   return 0;
115 }
116 
117 static int
cb_apop_safety_checks(void * data,mu_config_value_t * arg)118 cb_apop_safety_checks (void *data, mu_config_value_t *arg)
119 {
120   return mu_cfg_string_value_cb (arg, cb2_file_safety_checks,
121 				 &apop_database_safety);
122 }
123 
124 static int
cb_apop_database_owner(void * data,mu_config_value_t * val)125 cb_apop_database_owner (void *data, mu_config_value_t *val)
126 {
127   struct passwd *pw;
128 
129   if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
130     return 1;
131   pw = getpwnam (val->v.string);
132   if (!pw)
133     {
134       char *p;
135       unsigned long n;
136 
137       n = strtoul (val->v.string, &p, 10);
138       if (*p)
139 	{
140 	  mu_error (_("no such user: %s"), val->v.string);
141 	  return 1;
142 	}
143       apop_database_owner = n;
144     }
145   else
146     apop_database_owner = pw->pw_uid;
147   apop_database_owner_set = 1;
148   return 0;
149 }
150 
151 #ifdef ENABLE_DBM
152 static int
cb_bulletin_db(void * data,mu_config_value_t * val)153 cb_bulletin_db (void *data, mu_config_value_t *val)
154 {
155   if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
156     return 1;
157   set_bulletin_db (val->v.string); /* FIXME: Error reporting? */
158   return 0;
159 }
160 #endif
161 
162 static int
cb_tls(void * data,mu_config_value_t * val)163 cb_tls (void *data, mu_config_value_t *val)
164 {
165   int *res = data;
166   static struct mu_kwd tls_kwd[] = {
167     { "no", tls_no },
168     { "false", tls_no },
169     { "off", tls_no },
170     { "0", tls_no },
171     { "ondemand", tls_ondemand },
172     { "stls", tls_ondemand },
173     { "required", tls_required },
174     { "connection", tls_connection },
175     /* For compatibility with prior versions: */
176     { "yes", tls_connection },
177     { "true", tls_connection },
178     { "on", tls_connection },
179     { "1", tls_connection },
180     { NULL }
181   };
182 
183   if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
184     return 1;
185 
186   if (mu_kwd_xlat_name (tls_kwd, val->v.string, res))
187     mu_error (_("not a valid tls keyword: %s"), val->v.string);
188   return 0;
189 }
190 
191 static struct mu_cfg_param pop3d_srv_param[] = {
192   { "tls-mode", mu_cfg_callback,
193     NULL, mu_offsetof (struct pop3d_srv_config, tls_mode), cb_tls,
194     N_("Kind of TLS encryption to use for this server"),
195     /* TRANSLATORS: words to the right of : are keywords - do not translate */
196     N_("arg: false|true|ondemand|stls|required|connection") },
197   { "tls", mu_cfg_section,
198     NULL, mu_offsetof (struct pop3d_srv_config, tls_conf) },
199   { NULL }
200 };
201 
202 static struct mu_cfg_param pop3d_cfg_param[] = {
203   { "undelete", mu_c_bool, &undelete_on_startup, 0, NULL,
204     N_("On startup, clear deletion marks from all the messages.") },
205   { "expire", mu_c_uint, &expire, 0, NULL,
206     N_("Automatically expire read messages after the given number of days."),
207     N_("days") },
208   { "delete-expired", mu_c_bool, &expire_on_exit, 0, NULL,
209     N_("Delete expired messages upon closing the mailbox.") },
210   { "scan-lines", mu_c_bool, &pop3d_xlines, 0, NULL,
211     N_("Output the number of lines in the message in its scan listing.") },
212   { "apop-database-file", mu_c_string, &apop_database_name, 0, NULL,
213     N_("set APOP database file name or URL") },
214   { "apop-database-owner", mu_cfg_callback, NULL, 0, cb_apop_database_owner,
215     N_("Name or UID of the APOP database owner"),
216     N_("arg: string") },
217   { "apop-database-safety", mu_cfg_callback, NULL, 0, cb_apop_safety_checks,
218     N_("Configure safety checks for APOP database files.  Argument is a list or "
219        "sequence of check names optionally prefixed with '+' to enable or "
220        "'-' to disable the corresponding check.  Valid check names are:\n"
221        "\n"
222        "  none          disable all checks\n"
223        "  all           enable all checks\n"
224        "  gwrfil        forbid group writable files\n"
225        "  awrfil        forbid world writable files\n"
226        "  grdfil        forbid group readable files\n"
227        "  ardfil        forbid world writable files\n"
228        "  linkwrdir     forbid symbolic links in group or world writable directories\n"
229        "  gwrdir        forbid files in group writable directories\n"
230        "  awrdir        forbid files in world writable directories\n"),
231     N_("arg: list") },
232 
233   { "tls", mu_cfg_section, &global_tls_conf },
234   { "tls-mode", mu_cfg_callback,
235     &global_tls_mode, 0, cb_tls,
236     N_("Kind of TLS encryption to use for the inetd server"
237        " and all server blocks that lack the tls-mode statement."),
238     /* TRANSLATORS: words to the right of : are keywords - do not translate */
239     N_("arg: false|true|ondemand|stls|required|connection") },
240 
241 #ifdef ENABLE_LOGIN_DELAY
242   { "login-delay", mu_c_time, &login_delay, 0, NULL,
243     N_("Set the minimal allowed delay between two successive logins.") },
244   { "stat-file", mu_c_string, &login_stat_file, 0, NULL,
245     N_("Set the name of login statistics file (for login-delay).") },
246 #endif
247   { "bulletin-source", mu_cfg_callback, NULL, 0, cb_bulletin_source,
248     N_("Get bulletins from the specified mailbox."),
249     N_("url: string") },
250 #ifdef ENABLE_DBM
251   { "bulletin-db", mu_cfg_callback, NULL, 0, cb_bulletin_db,
252     N_("Set the bulletin database file name."),
253     N_("file: string") },
254 #endif
255   { "output-buffer-size", mu_c_size, &pop3d_output_bufsize, 0, NULL,
256     N_("Size of the output buffer.") },
257   { "mandatory-locking", mu_cfg_section },
258   { ".server", mu_cfg_section, NULL, 0, NULL,
259     N_("Server configuration.") },
260   { "transcript", mu_c_bool, &pop3d_transcript, 0, NULL,
261     N_("Set global transcript mode.") },
262   TCP_WRAPPERS_CONFIG
263   { NULL }
264 };
265 
266 static char *capa[] = {
267   "auth",
268   "debug",
269   "mailbox",
270   "locking",
271   "logging",
272   NULL
273 };
274 
275 struct mu_cli_setup cli = {
276   .optv = options,
277   .cfg = pop3d_cfg_param,
278   .prog_doc = N_("GNU pop3d -- the POP3 daemon."),
279   .server = 1
280 };
281 
282 int
pop3d_get_client_address(int fd,struct sockaddr_in * pcs)283 pop3d_get_client_address (int fd, struct sockaddr_in *pcs)
284 {
285   mu_diag_output (MU_DIAG_INFO, _("incoming connection opened"));
286 
287   /* log information on the connecting client. */
288   if (debug_mode)
289     {
290       mu_diag_output (MU_DIAG_INFO, _("started in debugging mode"));
291       return 1;
292     }
293   else
294     {
295       socklen_t len = sizeof *pcs;
296       if (getpeername (fd, (struct sockaddr*) pcs, &len) < 0)
297 	{
298 	  mu_diag_output (MU_DIAG_ERROR,
299 			  _("cannot obtain IP address of client: %s"),
300 			  strerror (errno));
301 	  return 1;
302 	}
303     }
304   return 0;
305 }
306 
307 /* The main part of the daemon. This function reads input from the client and
308    executes the proper functions. Also handles the bulk of error reporting.
309    Arguments:
310       ifd       --  input descriptor
311       ofd       --  output descriptor
312       tls       --  initiate encrypted connection */
313 int
pop3d_mainloop(int ifd,int ofd,struct pop3d_srv_config * cfg)314 pop3d_mainloop (int ifd, int ofd, struct pop3d_srv_config *cfg)
315 {
316   int status = OK;
317   char buffer[512];
318   static int sigtab[] = { SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSTOP, SIGPIPE,
319 			  SIGABRT, SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGALRM };
320   struct pop3d_session session;
321 
322   mu_set_signals (pop3d_child_signal, sigtab, MU_ARRAY_SIZE (sigtab));
323 
324   pop3d_setio (ifd, ofd,
325 	       cfg->tls_mode == tls_connection ? &cfg->tls_conf : NULL);
326 
327   if (cfg->tls_mode == tls_required)
328     initial_state = INITIAL;
329 
330   state = cfg->tls_mode == tls_connection ? AUTHORIZATION : initial_state;
331 
332   pop3d_session_init (&session);
333   session.tls_mode = cfg->tls_mode;
334   session.tls_conf = &cfg->tls_conf;
335 
336   /* FIXME: state should also be in the session? */
337 
338   /* Prepare the shared secret for APOP.  */
339   {
340     char *local_hostname;
341 
342     status = mu_get_host_name (&local_hostname);
343     if (status)
344       {
345         mu_diag_funcall (MU_DIAG_ERROR, "mu_get_host_name", NULL, status);
346         exit (EXIT_FAILURE);
347       }
348 
349     md5shared = mu_alloc (strlen (local_hostname) + 51);
350 
351     snprintf (md5shared, strlen (local_hostname) + 50, "<%u.%u@%s>", getpid (),
352 	      (unsigned)time (NULL), local_hostname);
353     free (local_hostname);
354   }
355 
356   /* Lets boogie.  */
357   pop3d_outf ("+OK POP3 Ready %s\n", md5shared);
358 
359   while (state != UPDATE && state != ABORT)
360     {
361       char *buf;
362       char *arg, *cmd;
363       pop3d_command_handler_t handler;
364 
365       pop3d_flush_output ();
366       status = OK;
367       buf = pop3d_readline (buffer, sizeof (buffer));
368       pop3d_parse_command (buf, &cmd, &arg);
369 
370       if (state == TRANSACTION && !mu_mailbox_is_updated (mbox))
371 	{
372 	  static mu_off_t mailbox_size;
373 	  mu_off_t newsize = 0;
374 	  mu_mailbox_get_size (mbox, &newsize);
375 	  /* Did we shrink?  First time save the size.  */
376 	  if (!mailbox_size)
377 	    mailbox_size = newsize;
378 	  else if (newsize < mailbox_size) /* FIXME: Should it be a != ? */
379 	    pop3d_abquit (ERR_MBOX_SYNC); /* Out of sync, Bail out.  */
380 	}
381 
382       /* Refresh the Lock.  */
383       manlock_touchlock (mbox);
384 
385       if ((handler = pop3d_find_command (cmd)) != NULL)
386 	status = handler (arg, &session);
387       else
388 	status = ERR_BAD_CMD;
389 
390       if (status != OK)
391 	pop3d_outf ("-ERR %s\n", pop3d_error_string (status));
392     }
393 
394   pop3d_session_free (&session);
395 
396   pop3d_bye ();
397 
398   return status;
399 }
400 
401 int
pop3d_connection(int fd,struct sockaddr * sa,int salen,struct mu_srv_config * pconf,void * data)402 pop3d_connection (int fd, struct sockaddr *sa, int salen,
403 		  struct mu_srv_config *pconf,
404 		  void *data)
405 {
406   struct pop3d_srv_config *cfg = (struct pop3d_srv_config *) pconf;
407 
408   idle_timeout = cfg->m_cfg.timeout;
409   pop3d_transcript = cfg->m_cfg.transcript;
410 
411   pop3d_mainloop (fd, fd, cfg);
412   return 0;
413 }
414 
415 static void
pop3d_alloc_die()416 pop3d_alloc_die ()
417 {
418   pop3d_abquit (ERR_NO_MEM);
419 }
420 
421 #ifdef ENABLE_DBM
422 static void
set_dbm_safety()423 set_dbm_safety ()
424 {
425   mu_url_t hints = mu_dbm_get_hint ();
426   const char *param[] = { "+all" };
427   mu_url_add_param (hints, 1, param);
428 }
429 #endif
430 
431 int
main(int argc,char ** argv)432 main (int argc, char **argv)
433 {
434   struct group *gr;
435   int status = OK;
436   static int sigtab[] = { SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSTOP, SIGPIPE };
437 
438   /* Native Language Support */
439   MU_APP_INIT_NLS ();
440 
441   MU_AUTH_REGISTER_ALL_MODULES();
442   /* Register the desired formats.  */
443   mu_register_local_mbox_formats ();
444 
445   mu_tcpwrapper_cfg_init ();
446   manlock_cfg_init ();
447   mu_acl_cfg_init ();
448   mu_tls_cfg_init ();
449 
450   mu_m_server_create (&server, program_version);
451   mu_m_server_set_config_size (server, sizeof (struct pop3d_srv_config));
452   mu_m_server_set_conn (server, pop3d_connection);
453   mu_m_server_set_prefork (server, mu_tcp_wrapper_prefork);
454   mu_m_server_set_mode (server, MODE_INTERACTIVE);
455   mu_m_server_set_max_children (server, 20);
456   /* FIXME mu_m_server_set_pidfile (); */
457   mu_m_server_set_default_port (server, 110);
458   mu_m_server_set_timeout (server, 600);
459   mu_m_server_set_strexit (server, mu_strexit);
460   mu_m_server_cfg_init (server, pop3d_srv_param);
461 
462   mu_alloc_die_hook = pop3d_alloc_die;
463 
464   mu_log_syslog = 1;
465   manlock_mandatory_locking = 1;
466 
467 #ifdef ENABLE_DBM
468   set_dbm_safety ();
469 #endif
470 
471   mu_cli (argc, argv, &cli, capa, server, &argc, &argv);
472   if (argc)
473     {
474       mu_error (_("too many arguments"));
475       exit (EX_USAGE);
476     }
477 
478   if (expire == 0)
479     expire_on_exit = 1;
480 
481 #ifdef USE_LIBPAM
482   if (!mu_pam_service)
483     mu_pam_service = "gnu-pop3d";
484 #endif
485 
486   if (mu_m_server_mode (server) == MODE_INTERACTIVE && isatty (0))
487     {
488       /* If input is a tty, switch to debug mode */
489       debug_mode = 1;
490     }
491   else
492     {
493       errno = 0;
494       gr = getgrnam ("mail");
495       if (gr == NULL)
496 	{
497 	  if (errno == 0 || errno == ENOENT)
498             {
499                mu_error (_("%s: no such group"), "mail");
500                exit (EX_CONFIG);
501             }
502           else
503             {
504 	      mu_diag_funcall (MU_DIAG_ERROR, "getgrnam", "mail", errno);
505 	      exit (EX_OSERR);
506             }
507 	}
508 
509       if (setgid (gr->gr_gid) == -1)
510 	{
511 	  mu_error (_("error setting mail group: %s"), mu_strerror (errno));
512 	  exit (EX_OSERR);
513 	}
514     }
515 
516   /* Set the signal handlers.  */
517   mu_set_signals (pop3d_master_signal, sigtab, MU_ARRAY_SIZE (sigtab));
518 
519   mu_stdstream_strerr_setup (mu_log_syslog ?
520 			     MU_STRERR_SYSLOG : MU_STRERR_STDERR);
521 
522   umask (S_IROTH | S_IWOTH | S_IXOTH);	/* 007 */
523 
524   /* Check TLS environment, i.e. cert and key files */
525   mu_m_server_set_preflight (server, stls_preflight);
526 
527   /* Actually run the daemon.  */
528   if (mu_m_server_mode (server) == MODE_DAEMON)
529     {
530       mu_m_server_begin (server);
531       status = mu_m_server_run (server);
532       mu_m_server_end (server);
533       mu_m_server_destroy (&server);
534     }
535   else
536     {
537       struct pop3d_srv_config cfg;
538       memset (&cfg, 0, sizeof cfg);
539 
540       idle_timeout = mu_m_server_timeout (server);
541 
542       switch (stls_server_check (&cfg, "<inetd>"))
543 	{
544 	case MU_TLS_CONFIG_OK:
545 	  if (mu_init_tls_libs ())
546 	    status = EX_OK;
547 	  else
548 	    {
549 	      mu_error (_("TLS is not configured, but requested in the "
550 			  "configuration"));
551 	      exit (EX_CONFIG);
552 	    }
553 	  break;
554 
555 	case MU_TLS_CONFIG_NULL:
556 	  break;
557 
558 	case MU_TLS_CONFIG_UNSAFE:
559 	  exit (EX_CONFIG);
560 
561 	default:
562 	  exit (EX_UNAVAILABLE);
563 	}
564 
565       /* Make sure we are in the root directory.  */
566       chdir ("/");
567       status = pop3d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, &cfg);
568     }
569 
570   if (status)
571     mu_error (_("main loop status: %s"), mu_strerror (status));
572   /* Close the syslog connection and exit.  */
573   closelog ();
574   return status ? EX_SOFTWARE : EX_OK;
575 }
576 
577