masqmail

view src/masqmail.c @ 378:5781ba87df95

Removed ident. This had been discussed on the mailing list in Oct 2011. Ident is hardly useful in typical setups for masqmail. Probably Oliver had used it in his setup; that would make sense. Now, I know of nobody who needs it.
author markus schnalke <meillo@marmaro.de>
date Sat, 14 Jan 2012 21:36:58 +0100
parents b27f66555ba8
children cdd16614c1f5
line source
1 /*
2 ** MasqMail
3 ** Copyright (C) 1999-2001 Oliver Kurth
4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
21 #include <stdio.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/time.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <syslog.h>
32 #include <signal.h>
34 #include <glib.h>
36 #include "masqmail.h"
38 /*
39 ** mutually exclusive modes. Note that there is no 'queue daemon' mode.
40 ** It, as well as the distinction beween the two (non exclusive) daemon
41 ** (queue and listen) modes, is handled by flags.
42 */
43 enum mta_mode {
44 MODE_NONE = 0, /* to check if a mode was set */
45 MODE_ACCEPT, /* accept message on stdin (fallback mode) */
46 MODE_DAEMON, /* run as daemon */
47 MODE_RUNQUEUE, /* single queue run, online or offline */
48 MODE_SMTP, /* accept SMTP on stdin */
49 MODE_LIST, /* list queue */
50 MODE_MCMD, /* do queue manipulation */
51 MODE_VERSION, /* show version */
52 MODE_BI, /* fake ;-) */
53 };
54 enum mta_mode mta_mode = MODE_NONE;
56 char *pidfile = NULL;
57 volatile int sigterm_in_progress = 0;
59 static void
60 sigterm_handler(int sig)
61 {
62 if (sigterm_in_progress)
63 raise(sig);
64 sigterm_in_progress = 1;
66 if (pidfile) {
67 uid_t uid = geteuid();
68 if (seteuid(0) != 0) {
69 logwrite(LOG_ALERT, "sigterm_handler: could not set euid to %d: %s\n", 0, strerror(errno));
70 }
71 if (unlink(pidfile) != 0)
72 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
73 seteuid(uid); /* we exit anyway after this, just to be sure */
74 }
76 signal(sig, SIG_DFL);
77 raise(sig);
78 }
80 /*
81 ** argv: the original argv
82 ** argp: number of arg (may get modified!)
83 ** cp: pointing to the char after the option
84 ** e.g. `-d 6' `-d6'
85 ** ^ ^
86 */
87 gchar*
88 get_optarg(char *argv[], gint *argp, char *cp)
89 {
90 if (*cp) {
91 /* this kind: -xval */
92 return cp;
93 }
94 cp = argv[*argp+1];
95 if (cp && (*cp != '-')) {
96 /* this kind: -x val */
97 (*argp)++;
98 return cp;
99 }
100 return NULL;
101 }
103 gboolean
104 write_pidfile(gchar *name)
105 {
106 FILE *fptr;
108 if ((fptr = fopen(name, "wt"))) {
109 fprintf(fptr, "%d\n", getpid());
110 fclose(fptr);
111 pidfile = strdup(name);
112 return TRUE;
113 }
114 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
115 return FALSE;
116 }
118 /* on -bd or if -q has an argument */
119 static void
120 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
121 {
122 guint pid;
124 /* daemon */
125 if (!conf.run_as_user) {
126 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
127 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
128 exit(1);
129 }
130 }
132 /* reparent to init only if init is not already the parent */
133 if (getppid() != 1) {
134 if ((pid = fork()) > 0) {
135 exit(0);
136 } else if (pid < 0) {
137 logwrite(LOG_ALERT, "could not fork!\n");
138 exit(1);
139 }
140 }
142 signal(SIGTERM, sigterm_handler);
143 write_pidfile(PIDFILEDIR "/masqmail.pid");
145 conf.do_verbose = FALSE;
147 /*
148 ** closing and reopening the log ensures that it is open afterwards
149 ** because it is possible that the log is assigned to fd 1 and gets
150 ** thus closes by fclose(stdout). Similar for the debugfile.
151 */
152 logclose();
153 fclose(stdin);
154 fclose(stdout);
155 fclose(stderr);
156 logopen();
158 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
159 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
160 }
162 /* -bs or called as smtpd or in.smtpd */
163 static void
164 mode_smtp()
165 {
166 /* accept smtp message on stdin */
167 /* write responses to stderr. */
169 struct sockaddr_in saddr;
170 gchar *peername = NULL;
171 int dummy = sizeof(saddr);
173 conf.do_verbose = FALSE;
175 if (!conf.run_as_user) {
176 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
177 }
179 DEBUG(5) debugf("accepting smtp message on stdin\n");
181 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
182 peername = g_strdup(inet_ntoa(saddr.sin_addr));
183 } else if (errno != ENOTSOCK)
184 exit(1);
186 smtp_in(stdin, stderr, peername, NULL);
187 }
189 /* default mode if address args or -t is specified, or called as rmail */
190 static void
191 mode_accept(address *return_path, gchar *full_sender_name, guint accept_flags,
192 char **addresses, int addr_cnt)
193 {
194 /* accept message on stdin */
195 accept_error err;
196 message *msg = create_message();
197 gint i;
198 pid_t pid;
200 if (return_path && !is_privileged_user(conf.orig_uid)) {
201 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
202 exit(1);
203 }
205 if (!conf.run_as_user) {
206 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
207 }
209 DEBUG(5) debugf("accepting message on stdin\n");
211 msg->received_prot = PROT_LOCAL;
213 /* warn if -t option and cmdline addr args */
214 if (addr_cnt && (accept_flags & ACC_RCPT_FROM_HEAD)) {
215 logwrite(LOG_ALERT, "command line address arguments are now *added* to the mail header\\\n");
216 logwrite(LOG_ALERT, " recipient addresses (instead of substracted) when -t is given.\\\n");
217 logwrite(LOG_ALERT, " this changed with version 0.3.1\n");
218 }
220 for (i = 0; i < addr_cnt; i++) {
221 if (addresses[i][0] == '|') {
222 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
223 /* should we better ignore this one addr? */
224 exit(1);
225 }
226 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
227 }
229 /* -f option */
230 msg->return_path = return_path;
232 /* -F option */
233 msg->full_sender_name = full_sender_name;
235 err = accept_message(stdin, msg, accept_flags);
237 switch (err) {
238 case AERR_OK:
239 /* to continue; all other cases exit */
240 break;
241 case AERR_EOF:
242 fprintf(stderr, "unexpected EOF.\n");
243 exit(1);
244 case AERR_NORCPT:
245 fprintf(stderr, "no recipients.\n");
246 exit(1);
247 case AERR_SIZE:
248 fprintf(stderr, "max message size exceeded.\n");
249 exit(1);
250 default:
251 /* should never happen: */
252 fprintf(stderr, "Unknown error (%d)\r\n", err);
253 exit(1);
254 }
256 if (!spool_write(msg, TRUE)) {
257 fprintf(stderr, "Could not write spool file\n");
258 exit(1);
259 }
261 /* here the mail is queued and thus in our responsibility */
262 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
264 if (conf.do_queue) {
265 /* we're finished as we only need to queue it */
266 return;
267 }
269 /* deliver at once */
270 if ((pid = fork()) < 0) {
271 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
272 } else if (pid == 0) {
273 conf.do_verbose = FALSE;
274 fclose(stdin);
275 fclose(stdout);
276 fclose(stderr);
277 if (deliver(msg)) {
278 exit(0);
279 } else {
280 /*
281 ** TODO: Should we really fail here? Because the
282 ** mail is queued already. If we fail the client
283 ** might submit it again. If at-once-delivery
284 ** is seen as an additional best-effort service,
285 ** then we should still exit successful here.
286 */
287 exit(1);
288 }
289 }
290 }
292 /*
293 ** if -Mrm is given
294 **
295 ** currently only the `rm' command is supported
296 ** until this changes, we don't need any facility for further commands
297 ** return success if at least one message had been deleted
298 */
299 static int
300 manipulate_queue(char *cmd, char *id[])
301 {
302 gboolean ok = FALSE;
304 if (strcmp(cmd, "rm") != 0) {
305 fprintf(stderr, "unknown command %s\n", cmd);
306 return FALSE;
307 }
309 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
311 /* privileged users may delete any mail */
312 if (is_privileged_user(conf.orig_uid)) {
313 for (; *id; id++) {
314 fprintf(stderr, "id: %s\n", *id);
315 if (queue_delete(*id)) {
316 ok = TRUE;
317 }
318 }
319 return ok;
320 }
322 struct passwd *pw = getpwuid(conf.orig_uid);
323 if (!pw) {
324 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
325 conf.orig_uid, strerror(errno));
326 return FALSE;
327 }
329 /* non-privileged users may only delete their own messages */
330 for (; *id; id++) {
331 message *msg = msg_spool_read(*id);
333 fprintf(stderr, "id: %s\n", *id);
335 if (!msg->ident) {
336 fprintf(stderr, "message %s does not have an ident\n", *id);
337 continue;
338 }
339 if (strcmp(pw->pw_name, msg->ident) != 0) {
340 fprintf(stderr, "you do not own message id %s\n", *id);
341 continue;
342 }
344 if (msg->received_host || (msg->received_prot != PROT_LOCAL)) {
345 fprintf(stderr, "message %s was not received locally\n", *id);
346 continue;
347 }
349 ok = queue_delete(*id);
350 }
351 return ok;
352 }
354 /* -qo, -q (without argument), or called as runq */
355 static int
356 run_queue(gboolean do_runq, gboolean do_runq_online, char *route_name)
357 {
358 int ret;
360 /* queue runs */
361 set_identity(conf.orig_uid, "queue run");
363 if (do_runq) {
364 ret = queue_run();
365 }
367 if (do_runq_online) {
368 if (route_name) {
369 conf.online_query = g_strdup_printf("/bin/echo %s", route_name);
370 }
371 /*
372 ** TODO: change behavior of `-qo without argument'?
373 ** Because that behavior is included in -q.
374 */
375 ret = queue_run_online();
376 }
377 return ret;
378 }
380 /* -bV or default mode if neither addr arg nor -t */
381 static void
382 mode_version(void)
383 {
384 gchar *with_resolver = "";
385 gchar *with_auth = "";
387 #ifdef ENABLE_RESOLVER
388 with_resolver = " +resolver";
389 #endif
390 #ifdef ENABLE_AUTH
391 with_auth = " +auth";
392 #endif
394 printf("%s %s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth);
395 }
397 void
398 set_mode(enum mta_mode mode)
399 {
400 if (mta_mode && mta_mode!=mode) {
401 fprintf(stderr, "operation mode was already specified (%d vs. %d)\n", mta_mode, mode);
402 exit(1);
403 }
405 mta_mode = mode;
406 return;
407 }
409 int
410 main(int argc, char *argv[])
411 {
412 gchar *progname;
413 char *opt;
414 gint arg;
416 gboolean do_listen = FALSE;
417 gboolean do_runq = FALSE;
418 gboolean do_runq_online = FALSE;
419 gboolean do_queue = FALSE;
420 gint queue_interval = 0;
421 gchar *M_cmd = NULL;
422 gboolean opt_t = FALSE;
423 gboolean opt_i = FALSE;
424 gchar *conf_file = CONF_FILE;
425 gchar *route_name = NULL;
426 gchar *f_address = NULL;
427 address *return_path = NULL; /* may be changed by -f option */
428 gchar *full_sender_name = NULL;
429 gboolean do_verbose = FALSE;
430 gint debug_level = -1;
432 /* strip the path part */
433 progname = strrchr(argv[0], '/');
434 progname = (progname) ? progname+1 : argv[0];
436 if (strcmp(progname, "mailq") == 0) {
437 mta_mode = MODE_LIST;
438 } else if (strcmp(progname, "mailrm") == 0) {
439 mta_mode = MODE_MCMD;
440 M_cmd = "rm";
441 } else if (strcmp(progname, "newaliases") == 0) {
442 mta_mode = MODE_BI;
443 } else if (strcmp(progname, "rmail") == 0) {
444 /*
445 ** the `rmail' alias should probably be removed now
446 ** that we have the rmail script. But let's keep it
447 ** for some while for compatibility. 2010-06-19
448 */
449 mta_mode = MODE_ACCEPT;
450 opt_i = TRUE;
451 } else if (strcmp(progname, "runq") == 0) {
452 mta_mode = MODE_RUNQUEUE;
453 do_runq = TRUE;
454 } else if (strcmp(progname, "smtpd") == 0
455 || strcmp(progname, "in.smtpd") == 0) {
456 mta_mode = MODE_SMTP;
457 }
459 /* parse cmd line */
460 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
461 opt = argv[arg] + 1; /* points to the char after the dash */
463 if (strcmp(opt, "-") == 0) {
464 /* everything after `--' are address arguments */
465 arg++;
466 break;
468 } else if (strcmp(opt, "bm") == 0) {
469 set_mode(MODE_ACCEPT);
471 } else if (strcmp(opt, "bd") == 0) {
472 set_mode(MODE_DAEMON);
473 do_listen = TRUE;
475 } else if (strcmp(opt, "bi") == 0) {
476 set_mode(MODE_BI);
478 } else if (strcmp(opt, "bs") == 0) {
479 set_mode(MODE_SMTP);
481 } else if (strcmp(opt, "bp") == 0) {
482 set_mode(MODE_LIST);
484 } else if (strcmp(opt, "bV") == 0) {
485 set_mode(MODE_VERSION);
487 } else if (strncmp(opt, "B", 1) == 0) {
488 /* we ignore this and throw the argument away */
489 get_optarg(argv, &arg, opt+1);
491 } else if (strncmp(opt, "C", 1) == 0) {
492 conf_file = get_optarg(argv, &arg, opt+1);
493 if (!conf_file) {
494 fprintf(stderr, "-C requires a filename as argument.\n");
495 exit(1);
496 }
498 } else if (strncmp(opt, "d", 1) == 0) {
499 if (getuid() != 0) {
500 fprintf(stderr, "only root may set the debug level.\n");
501 exit(1);
502 }
503 char *lvl = get_optarg(argv, &arg, opt+1);
504 if (!lvl) {
505 fprintf(stderr, "-d requires a number argument.\n");
506 exit(1);
507 }
508 debug_level = atoi(lvl);
510 } else if (strncmp(opt, "f", 1) == 0) {
511 /* set return path */
512 gchar *address = get_optarg(argv, &arg, opt+1);
513 if (!address) {
514 fprintf(stderr, "-f requires an address argument\n");
515 exit(1);
516 }
517 f_address = g_strdup(address);
519 } else if (strncmp(opt, "F", 1) == 0) {
520 full_sender_name = get_optarg(argv, &arg, opt+1);
521 if (!full_sender_name) {
522 fprintf(stderr, "-F requires a name argument\n");
523 exit(1);
524 }
526 } else if (strcmp(opt, "i") == 0) {
527 opt_i = TRUE;
529 } else if (strcmp(opt, "m") == 0) {
530 /* ignore -m (me too) switch (see man page) */
532 } else if (strcmp(opt, "Mrm") == 0) {
533 set_mode(MODE_MCMD);
534 M_cmd = "rm";
536 } else if (strcmp(opt, "odq") == 0) {
537 do_queue = TRUE;
539 } else if (strcmp(opt, "oi") == 0) {
540 opt_i = TRUE;
542 } else if (strncmp(opt, "o", 1) == 0) {
543 /* ignore all other -oXXX options */
545 } else if (strncmp(opt, "qo", 2) == 0) {
546 /* must be before the `q' check */
547 set_mode(MODE_RUNQUEUE);
548 do_runq_online = TRUE;
549 /* can be NULL, then we use online detection method */
550 /* TODO: behavior might change if it is NULL */
551 route_name = get_optarg(argv, &arg, opt+2);
552 if (!route_name) {
553 fprintf(stderr, "Please do not use -qo without argument anymore; use -q instead.\n");
554 fprintf(stderr, "The behavior for -qo without argument is likely to change.\n");
555 }
557 } else if (strncmp(opt, "q", 1) == 0) {
558 /* must be after the `qo' check */
559 gchar *optarg;
561 optarg = get_optarg(argv, &arg, opt+1);
562 if (optarg) {
563 /* not just one single queue run but regular runs */
564 set_mode(MODE_DAEMON);
565 queue_interval = time_interval(optarg);
566 } else {
567 set_mode(MODE_RUNQUEUE);
568 do_runq = TRUE;
569 }
571 } else if (strcmp(opt, "t") == 0) {
572 opt_t = TRUE;
574 } else if (strcmp(opt, "v") == 0) {
575 do_verbose = TRUE;
577 } else {
578 fprintf(stderr, "unrecognized option `-%s'\n", opt);
579 exit(1);
580 }
581 }
583 if (!mta_mode && arg==argc && !opt_t) {
584 /*
585 ** In this case no rcpts can be found, thus no mail
586 ** can be sent, thus masqmail will always fail. We
587 ** rather do something better instead. This covers
588 ** also the case of calling masqmail without args.
589 */
590 mode_version();
591 exit(0);
592 }
594 if (mta_mode == MODE_VERSION) {
595 mode_version();
596 exit(0);
597 }
599 if (!mta_mode) {
600 mta_mode = MODE_ACCEPT;
601 }
603 /* initialize random generator */
604 srand(time(NULL));
605 /* ignore SIGPIPE signal */
606 signal(SIGPIPE, SIG_IGN);
608 /* close all possibly open file descriptors, except std{in,out,err} */
609 {
610 int i, max_fd = sysconf(_SC_OPEN_MAX);
612 if (max_fd <= 0) {
613 max_fd = 64;
614 }
615 for (i=3; i<max_fd; i++) {
616 close(i);
617 }
618 }
620 init_conf();
622 /*
623 ** if we are not privileged, and the config file was changed we
624 ** implicetely set the the run_as_user flag and give up all
625 ** privileges.
626 **
627 ** So it is possible for a user to run his own daemon without
628 ** breaking security.
629 */
630 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
631 conf.run_as_user = TRUE;
632 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
633 if (setgid(conf.orig_gid)) {
634 logwrite(LOG_ALERT, "could not set gid to %d: %s\n", conf.orig_gid, strerror(errno));
635 exit(1);
636 }
637 if (setuid(conf.orig_uid)) {
638 logwrite(LOG_ALERT, "could not set uid to %d: %s\n", conf.orig_uid, strerror(errno));
639 exit(1);
640 }
641 }
643 conf.log_dir = LOG_DIR;
644 /* FIXME: fails if we run as user */
645 logopen();
646 if (!read_conf(conf_file)) {
647 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
648 exit(5);
649 }
650 logclose();
652 if (do_queue) {
653 conf.do_queue = TRUE;
654 }
655 if (do_verbose) {
656 conf.do_verbose = TRUE;
657 }
658 if (debug_level >= 0) { /* if >= 0, it was given by argument */
659 conf.debug_level = debug_level;
660 }
662 /*
663 ** It appears that changing to / ensures that we are never in
664 ** a directory which we cannot access. This situation could be
665 ** possible after changing identity.
666 ** Maybe we should only change to / if we not run as user, to
667 ** allow relative paths for log files in test setups for
668 ** instance.
669 */
670 chdir("/");
672 if (!conf.run_as_user) {
673 if (setgid(0) != 0) {
674 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
675 exit(1);
676 }
677 if (setuid(0) != 0) {
678 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
679 exit(1);
680 }
681 }
683 if (!logopen()) {
684 fprintf(stderr, "could not open log file\n");
685 exit(1);
686 }
688 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
690 DEBUG(5) {
691 gchar **str = argv;
692 debugf("args: \n");
693 while (*str) {
694 debugf("%s \n", *str);
695 str++;
696 }
697 }
698 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
700 if (f_address) {
701 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
702 g_free(f_address);
703 if (!return_path) {
704 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
705 exit(1);
706 }
707 }
709 switch (mta_mode) {
710 case MODE_DAEMON:
711 mode_daemon(do_listen, queue_interval, argv);
712 break;
714 case MODE_RUNQUEUE:
715 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
716 break;
718 case MODE_SMTP:
719 mode_smtp();
720 break;
722 case MODE_LIST:
723 queue_list();
724 break;
726 case MODE_BI:
727 exit(0);
728 break; /* well... */
730 case MODE_MCMD:
731 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
732 break;
734 case MODE_ACCEPT:
735 {
736 guint accept_flags = (opt_t ? ACC_RCPT_FROM_HEAD : 0)
737 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
738 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
739 exit(0);
740 }
741 break;
743 default:
744 fprintf(stderr, "unknown mode: %d\n", mta_mode);
745 break;
746 }
748 logclose();
750 exit(0);
751 }