masqmail

view src/masqmail.c @ 204:5745edd5b769

removed the --disable-smtp-server configure option masqmail will now always have the possibility to listen on a port compiled in if you don't want it to listen on a port add this to your masqmail.conf: listen_addresses = "" also, if you don't start it as daemon it will not listen neither
author meillo@marmaro.de
date Fri, 16 Jul 2010 15:38:53 +0200
parents 89f951be358f
children 4fd237550525
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #include <stdio.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/time.h>
28 #include <netinet/in.h>
29 #include <netdb.h>
30 #include <syslog.h>
31 #include <signal.h>
33 #include <glib.h>
35 #include "masqmail.h"
37 /* mutually exclusive modes. Note that there is no 'queue daemon' mode.
38 It, as well as the distinction beween the two (non exclusive) daemon
39 (queue and listen) modes, is handled by flags.*/
40 typedef enum _mta_mode {
41 MODE_ACCEPT = 0, /* accept message on stdin */
42 MODE_DAEMON, /* run as daemon */
43 MODE_RUNQUEUE, /* single queue run, online or offline */
44 MODE_SMTP, /* accept SMTP on stdin */
45 MODE_LIST, /* list queue */
46 MODE_MCMD, /* do queue manipulation */
47 MODE_VERSION, /* show version */
48 MODE_BI, /* fake ;-) */
49 MODE_NONE /* to prevent default MODE_ACCEPT */
50 } mta_mode;
52 char *pidfile = NULL;
53 volatile int sigterm_in_progress = 0;
55 static void
56 sigterm_handler(int sig)
57 {
58 if (sigterm_in_progress)
59 raise(sig);
60 sigterm_in_progress = 1;
62 if (pidfile) {
63 uid_t uid;
64 uid = seteuid(0);
65 if (unlink(pidfile) != 0)
66 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
67 seteuid(uid); /* we exit anyway after this, just to be sure */
68 }
70 signal(sig, SIG_DFL);
71 raise(sig);
72 }
74 #ifdef ENABLE_IDENT /* so far used for that only */
75 static gboolean
76 is_in_netlist(gchar * host, GList * netlist)
77 {
78 guint hostip = inet_addr(host);
79 struct in_addr addr;
81 addr.s_addr = hostip;
82 if (addr.s_addr != INADDR_NONE) {
83 GList *node;
84 foreach(netlist, node) {
85 struct in_addr *net = (struct in_addr *) (node->data);
86 if ((addr.s_addr & net->s_addr) == net->s_addr)
87 return TRUE;
88 }
89 }
90 return FALSE;
91 }
92 #endif
94 gchar*
95 get_optarg(char *argv[], gint argc, gint * argp, gint * pos)
96 {
97 if (argv[*argp][*pos])
98 return &(argv[*argp][*pos]);
99 else {
100 if (*argp + 1 < argc) {
101 if (argv[(*argp) + 1][0] != '-') {
102 (*argp)++;
103 *pos = 0;
104 return &(argv[*argp][*pos]);
105 }
106 }
107 }
108 return NULL;
109 }
111 gchar*
112 get_progname(gchar * arg0)
113 {
114 gchar *p = arg0 + strlen(arg0) - 1;
115 while (p > arg0) {
116 if (*p == '/')
117 return p + 1;
118 p--;
119 }
120 return p;
121 }
123 gboolean
124 write_pidfile(gchar * name)
125 {
126 FILE *fptr;
128 if ((fptr = fopen(name, "wt"))) {
129 fprintf(fptr, "%d\n", getpid());
130 fclose(fptr);
131 pidfile = strdup(name);
132 return TRUE;
133 }
134 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
135 return FALSE;
136 }
138 static void
139 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
140 {
141 guint pid;
143 /* daemon */
144 if (!conf.run_as_user) {
145 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
146 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
147 exit(EXIT_FAILURE);
148 }
149 }
151 /* reparent to init only if init is not already the parent */
152 if (getppid() != 1) {
153 if ((pid = fork()) > 0) {
154 exit(EXIT_SUCCESS);
155 } else if (pid < 0) {
156 logwrite(LOG_ALERT, "could not fork!");
157 exit(EXIT_FAILURE);
158 }
159 }
161 signal(SIGTERM, sigterm_handler);
162 write_pidfile(PIDFILEDIR "/masqmail.pid");
164 conf.do_verbose = FALSE;
166 /* closing and reopening the log ensures that it is open afterwards
167 because it is possible that the log is assigned to fd 1 and gets
168 thus closes by fclose(stdout). Similar for the debugfile.
169 */
170 logclose();
171 fclose(stdin);
172 fclose(stdout);
173 fclose(stderr);
174 logopen();
176 logwrite(LOG_NOTICE, "%s %s daemon starting", PACKAGE, VERSION);
177 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
178 }
180 static void
181 mode_smtp()
182 {
183 /* accept smtp message on stdin */
184 /* write responses to stderr. */
186 struct sockaddr_in saddr;
187 gchar *peername = NULL;
188 int dummy = sizeof(saddr);
190 conf.do_verbose = FALSE;
192 if (!conf.run_as_user) {
193 seteuid(conf.orig_uid);
194 setegid(conf.orig_gid);
195 }
197 DEBUG(5) debugf("accepting smtp message on stdin\n");
199 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
200 peername = g_strdup(inet_ntoa(saddr.sin_addr));
201 } else if (errno != ENOTSOCK)
202 exit(EXIT_FAILURE);
204 smtp_in(stdin, stderr, peername, NULL);
205 }
207 static void
208 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
209 {
210 /* accept message on stdin */
211 accept_error err;
212 message *msg = create_message();
213 gint i;
215 if (return_path && !is_privileged_user(conf.orig_uid)) {
216 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
217 exit(EXIT_FAILURE);
218 }
220 if (!conf.run_as_user) {
221 seteuid(conf.orig_uid);
222 setegid(conf.orig_gid);
223 }
225 DEBUG(5) debugf("accepting message on stdin\n");
227 msg->received_prot = PROT_LOCAL;
228 for (i = 0; i < addr_cnt; i++) {
229 if (addresses[i][0] != '|')
230 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
231 else {
232 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
233 exit(EXIT_FAILURE);
234 }
235 }
237 /* -f option */
238 msg->return_path = return_path;
240 /* -F option */
241 msg->full_sender_name = full_sender_name;
243 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
244 if (spool_write(msg, TRUE)) {
245 pid_t pid;
246 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
248 if (!conf.do_queue) {
249 if ((pid = fork()) == 0) {
250 conf.do_verbose = FALSE;
251 fclose(stdin);
252 fclose(stdout);
253 fclose(stderr);
254 if (deliver(msg)) {
255 exit(EXIT_SUCCESS);
256 } else
257 exit(EXIT_FAILURE);
258 } else if (pid < 0) {
259 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
260 }
261 }
262 } else {
263 fprintf(stderr, "Could not write spool file\n");
264 exit(EXIT_FAILURE);
265 }
266 } else {
267 switch (err) {
268 case AERR_EOF:
269 fprintf(stderr, "unexpected EOF.\n");
270 exit(EXIT_FAILURE);
271 case AERR_NORCPT:
272 fprintf(stderr, "no recipients.\n");
273 exit(EXIT_FAILURE);
274 case AERR_SIZE:
275 fprintf(stderr, "max message size exceeded.\n");
276 exit(EXIT_FAILURE);
277 default:
278 /* should never happen: */
279 fprintf(stderr, "Unknown error (%d)\r\n", err);
280 exit(EXIT_FAILURE);
281 }
282 exit(EXIT_FAILURE);
283 }
284 }
286 int
287 main(int argc, char *argv[])
288 {
289 /* cmd line flags */
290 gchar *conf_file = CONF_FILE;
291 gint arg = 1;
293 gboolean do_listen = FALSE;
294 gboolean do_runq = FALSE;
295 gboolean do_runq_online = FALSE;
297 gboolean do_queue = FALSE;
299 gboolean do_verbose = FALSE;
300 gint debug_level = -1;
302 mta_mode mta_mode = MODE_ACCEPT;
304 gint queue_interval = 0;
305 gboolean opt_t = FALSE;
306 gboolean opt_i = FALSE;
307 gboolean opt_odb = FALSE;
308 gboolean opt_oem = FALSE;
309 gboolean exit_failure = FALSE;
311 gchar *M_cmd = NULL;
313 gint exit_code = EXIT_SUCCESS;
314 gchar *route_name = NULL;
315 gchar *progname;
316 gchar *f_address = NULL;
317 gchar *full_sender_name = NULL;
318 address *return_path = NULL; /* may be changed by -f option */
320 progname = get_progname(argv[0]);
322 if (strcmp(progname, "mailq") == 0) {
323 mta_mode = MODE_LIST;
324 } else if (strcmp(progname, "mailrm") == 0) {
325 mta_mode = MODE_MCMD;
326 M_cmd = "rm";
327 } else if (strcmp(progname, "runq") == 0) {
328 mta_mode = MODE_RUNQUEUE;
329 do_runq = TRUE;
330 } else if (strcmp(progname, "rmail") == 0) {
331 /* the `rmail' alias should probably be removed now
332 that we have the rmail script. But let's keep it
333 for some while for compatibility. 2010-06-19 */
334 mta_mode = MODE_ACCEPT;
335 opt_i = TRUE;
336 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
337 mta_mode = MODE_SMTP;
338 }
340 /* parse cmd line */
341 while (arg < argc) {
342 gint pos = 0;
343 if ((argv[arg][pos] == '-') && (argv[arg][pos + 1] != '-')) {
344 pos++;
345 switch (argv[arg][pos++]) {
346 case 'b':
347 switch (argv[arg][pos++]) {
348 case 'd':
349 do_listen = TRUE;
350 mta_mode = MODE_DAEMON;
351 break;
352 case 'i':
353 /* ignored */
354 mta_mode = MODE_BI;
355 break;
356 case 's':
357 mta_mode = MODE_SMTP;
358 break;
359 case 'p':
360 mta_mode = MODE_LIST;
361 break;
362 case 'V':
363 mta_mode = MODE_VERSION;
364 break;
365 default:
366 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
367 exit(EXIT_FAILURE);
368 }
369 break;
370 case 'B':
371 /* we ignore this and throw the argument away */
372 get_optarg(argv, argc, &arg, &pos);
373 break;
374 case 'C':
375 if (!(conf_file = get_optarg(argv, argc, &arg, &pos))) {
376 fprintf(stderr, "-C requires a filename as argument.\n");
377 exit(EXIT_FAILURE);
378 }
379 break;
380 case 'F':
381 {
382 full_sender_name = get_optarg(argv, argc, &arg, &pos);
383 if (!full_sender_name) {
384 fprintf(stderr, "-F requires a name as an argument\n");
385 exit(EXIT_FAILURE);
386 }
387 }
388 break;
389 case 'd':
390 if (getuid() == 0) {
391 char *lvl = get_optarg(argv, argc, &arg, &pos);
392 if (lvl)
393 debug_level = atoi(lvl);
394 else {
395 fprintf(stderr, "-d requires a number as an argument.\n");
396 exit(EXIT_FAILURE);
397 }
398 } else {
399 fprintf(stderr, "only root may set the debug level.\n");
400 exit(EXIT_FAILURE);
401 }
402 break;
403 case 'f':
404 /* set return path */
405 {
406 gchar *address;
407 address = get_optarg(argv, argc, &arg, &pos);
408 if (address) {
409 f_address = g_strdup(address);
410 } else {
411 fprintf(stderr, "-f requires an address as an argument\n");
412 exit(EXIT_FAILURE);
413 }
414 }
415 break;
416 case 'i':
417 if (argv[arg][pos] == 0) {
418 opt_i = TRUE;
419 exit_failure = FALSE; /* may override -oem */
420 } else {
421 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
422 exit(EXIT_FAILURE);
423 }
424 break;
425 case 'M':
426 {
427 mta_mode = MODE_MCMD;
428 M_cmd = g_strdup(&(argv[arg][pos]));
429 }
430 break;
431 case 'o':
432 switch (argv[arg][pos++]) {
433 case 'e':
434 if (argv[arg][pos++] == 'm') /* -oem */
435 if (!opt_i)
436 exit_failure = TRUE;
437 opt_oem = TRUE;
438 break;
439 case 'd':
440 if (argv[arg][pos] == 'b') /* -odb */
441 opt_odb = TRUE;
442 else if (argv[arg][pos] == 'q') /* -odq */
443 do_queue = TRUE;
444 break;
445 case 'i':
446 opt_i = TRUE;
447 exit_failure = FALSE; /* may override -oem */
448 break;
449 }
450 break;
452 case 'q':
453 {
454 gchar *optarg;
456 do_runq = TRUE;
457 mta_mode = MODE_RUNQUEUE;
458 if (argv[arg][pos] == 'o') {
459 pos++;
460 do_runq = FALSE;
461 do_runq_online = TRUE;
462 /* can be NULL, then we use online detection method */
463 route_name = get_optarg(argv, argc, &arg, &pos);
464 } else
465 if ((optarg = get_optarg(argv, argc, &arg, &pos))) {
466 mta_mode = MODE_DAEMON;
467 queue_interval = time_interval(optarg, &pos);
468 }
469 }
470 break;
471 case 't':
472 if (argv[arg][pos] == 0) {
473 opt_t = TRUE;
474 } else {
475 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
476 exit(EXIT_FAILURE);
477 }
478 break;
479 case 'v':
480 do_verbose = TRUE;
481 break;
482 default:
483 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
484 exit(EXIT_FAILURE);
485 }
486 } else {
487 if (argv[arg][pos + 1] == '-') {
488 if (argv[arg][pos + 2] != '\0') {
489 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
490 exit(EXIT_FAILURE);
491 }
492 arg++;
493 }
494 break;
495 }
496 arg++;
497 }
499 if (mta_mode == MODE_VERSION) {
500 gchar *with_resolver = "";
501 gchar *with_auth = "";
502 gchar *with_maildir = "";
503 gchar *with_ident = "";
505 #ifdef ENABLE_RESOLVER
506 with_resolver = " +resolver";
507 #endif
508 #ifdef ENABLE_AUTH
509 with_auth = " +auth";
510 #endif
511 #ifdef ENABLE_MAILDIR
512 with_maildir = " +maildir";
513 #endif
514 #ifdef ENABLE_IDENT
515 with_ident = " +ident";
516 #endif
518 printf("%s %s%s%s%s%s\n", PACKAGE, VERSION, with_resolver,
519 with_auth, with_maildir, with_ident);
521 exit(EXIT_SUCCESS);
522 }
524 /* initialize random generator */
525 srand(time(NULL));
526 /* ignore SIGPIPE signal */
527 signal(SIGPIPE, SIG_IGN);
529 /* close all possibly open file descriptors, except std{in,out,err} */
530 {
531 int i, max_fd = sysconf(_SC_OPEN_MAX);
533 if (max_fd <= 0)
534 max_fd = 64;
535 for (i = 3; i < max_fd; i++)
536 close(i);
537 }
539 init_conf();
541 /* if we are not privileged, and the config file was changed we
542 implicetely set the the run_as_user flag and give up all
543 privileges.
545 So it is possible for a user to run his own daemon without
546 breaking security.
547 */
548 if (strcmp(conf_file, CONF_FILE) != 0) {
549 if (conf.orig_uid != 0) {
550 conf.run_as_user = TRUE;
551 seteuid(conf.orig_uid);
552 setegid(conf.orig_gid);
553 setuid(conf.orig_uid);
554 setgid(conf.orig_gid);
555 }
556 }
558 conf.log_dir = LOG_DIR;
559 logopen();
560 if (!read_conf(conf_file)) {
561 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
562 exit(5);
563 }
564 logclose();
566 if (do_queue)
567 conf.do_queue = TRUE;
568 if (do_verbose)
569 conf.do_verbose = TRUE;
570 if (debug_level >= 0) /* if >= 0, it was given by argument */
571 conf.debug_level = debug_level;
573 /* It appears that changing to / ensures that we are never in
574 a directory which we cannot access. This situation could be
575 possible after changing identity.
576 Maybe we should only change to / if we not run as user, to
577 allow relative paths for log files in test setups for
578 instance.
579 */
580 chdir("/");
582 if (!conf.run_as_user) {
583 if (setgid(0) != 0) {
584 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
585 exit(EXIT_FAILURE);
586 }
587 if (setuid(0) != 0) {
588 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
589 exit(EXIT_FAILURE);
590 }
591 }
593 if (!logopen()) {
594 fprintf(stderr, "could not open log file\n");
595 exit(EXIT_FAILURE);
596 }
598 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
600 DEBUG(5) {
601 gchar **str = argv;
602 debugf("args: \n");
603 while (*str) {
604 debugf("%s \n", *str);
605 str++;
606 }
607 }
608 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
610 if (f_address) {
611 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
612 g_free(f_address);
613 if (!return_path) {
614 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
615 exit(EXIT_FAILURE);
616 }
617 }
619 switch (mta_mode) {
620 case MODE_DAEMON:
621 mode_daemon(do_listen, queue_interval, argv);
622 break;
623 case MODE_RUNQUEUE:
624 {
625 /* queue runs */
626 set_identity(conf.orig_uid, "queue run");
628 if (do_runq)
629 exit_code = queue_run() ? EXIT_SUCCESS : EXIT_FAILURE;
631 if (do_runq_online) {
632 if (route_name != NULL) {
633 conf.online_detect = g_strdup("argument");
634 set_online_name(route_name);
635 }
636 exit_code =
637 queue_run_online() ? EXIT_SUCCESS : EXIT_FAILURE;
638 }
639 }
640 break;
642 case MODE_SMTP:
643 mode_smtp();
644 break;
646 case MODE_LIST:
647 queue_list();
648 break;
650 case MODE_BI:
651 exit(EXIT_SUCCESS);
652 break; /* well... */
654 case MODE_MCMD:
655 if (strcmp(M_cmd, "rm") == 0) {
656 gboolean ok = FALSE;
658 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
660 if (is_privileged_user(conf.orig_uid)) {
661 for (; arg < argc; arg++) {
662 if (queue_delete(argv[arg]))
663 ok = TRUE;
664 }
665 } else {
666 struct passwd *pw = getpwuid(conf.orig_uid);
667 if (pw) {
668 for (; arg < argc; arg++) {
669 message *msg = msg_spool_read(argv[arg], FALSE);
670 #ifdef ENABLE_IDENT
671 if (((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL))
672 || is_in_netlist(msg->received_host, conf.ident_trusted_nets)) {
673 #else
674 if ((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL)) {
675 #endif
676 if (msg->ident) {
677 if (strcmp(pw->pw_name, msg->ident) == 0) {
678 if (queue_delete(argv[arg]))
679 ok = TRUE;
680 } else {
681 fprintf(stderr, "you do not own message id %s\n", argv[arg]);
682 }
683 } else
684 fprintf(stderr, "message %s does not have an ident.\n", argv[arg]);
685 } else {
686 fprintf(stderr, "message %s was not received locally or from a trusted network.\n", argv[arg]);
687 }
688 }
689 } else {
690 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n", conf.orig_uid, strerror(errno));
691 }
692 }
693 exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
694 } else {
695 fprintf(stderr, "unknown command %s\n", M_cmd);
696 exit(EXIT_FAILURE);
697 }
698 break;
700 case MODE_ACCEPT:
701 {
702 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
703 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
704 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
705 exit(exit_failure ? EXIT_FAILURE : EXIT_SUCCESS);
706 }
707 break;
708 case MODE_NONE:
709 break;
710 default:
711 fprintf(stderr, "unknown mode: %d\n", mta_mode);
712 break;
713 }
715 logclose();
717 exit(exit_code);
718 }