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