masqmail
view src/masqmail.c @ 279:1aa107c6b1e5
moved some code around
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Mon, 06 Dec 2010 17:46:24 -0300 |
parents | 1abc1faeb45d |
children | ea5f86e0a81c |
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 (fallback mode) */
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 } mta_mode;
51 char *pidfile = NULL;
52 volatile int sigterm_in_progress = 0;
54 static void
55 sigterm_handler(int sig)
56 {
57 if (sigterm_in_progress)
58 raise(sig);
59 sigterm_in_progress = 1;
61 if (pidfile) {
62 uid_t uid;
63 uid = seteuid(0);
64 if (unlink(pidfile) != 0)
65 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
66 seteuid(uid); /* we exit anyway after this, just to be sure */
67 }
69 signal(sig, SIG_DFL);
70 raise(sig);
71 }
73 #ifdef ENABLE_IDENT /* so far used for that only */
74 static gboolean
75 is_in_netlist(gchar * host, GList * netlist)
76 {
77 guint hostip = inet_addr(host);
78 struct in_addr addr;
80 addr.s_addr = hostip;
81 if (addr.s_addr != INADDR_NONE) {
82 GList *node;
83 foreach(netlist, node) {
84 struct in_addr *net = (struct in_addr *) (node->data);
85 if ((addr.s_addr & net->s_addr) == net->s_addr)
86 return TRUE;
87 }
88 }
89 return FALSE;
90 }
91 #endif
93 /*
94 argv: the original argv
95 argp: number of arg (may get modified!)
96 cp: pointing to the char after the option
97 e.g. `-d 6' `-d6'
98 ^ ^
99 */
100 gchar*
101 get_optarg(char* argv[], gint* argp, char* cp)
102 {
103 if (*cp) {
104 /* this kind: -xval */
105 return cp;
106 }
107 cp = argv[*argp+1];
108 if (cp && (*cp != '-')) {
109 /* this kind: -x val */
110 (*argp)++;
111 return cp;
112 }
113 return NULL;
114 }
116 gboolean
117 write_pidfile(gchar * name)
118 {
119 FILE *fptr;
121 if ((fptr = fopen(name, "wt"))) {
122 fprintf(fptr, "%d\n", getpid());
123 fclose(fptr);
124 pidfile = strdup(name);
125 return TRUE;
126 }
127 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
128 return FALSE;
129 }
131 /* on -bd or if -q has an argument */
132 static void
133 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
134 {
135 guint pid;
137 /* daemon */
138 if (!conf.run_as_user) {
139 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
140 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
141 exit(1);
142 }
143 }
145 /* reparent to init only if init is not already the parent */
146 if (getppid() != 1) {
147 if ((pid = fork()) > 0) {
148 exit(0);
149 } else if (pid < 0) {
150 logwrite(LOG_ALERT, "could not fork!\n");
151 exit(1);
152 }
153 }
155 signal(SIGTERM, sigterm_handler);
156 write_pidfile(PIDFILEDIR "/masqmail.pid");
158 conf.do_verbose = FALSE;
160 /* closing and reopening the log ensures that it is open afterwards
161 because it is possible that the log is assigned to fd 1 and gets
162 thus closes by fclose(stdout). Similar for the debugfile.
163 */
164 logclose();
165 fclose(stdin);
166 fclose(stdout);
167 fclose(stderr);
168 logopen();
170 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
171 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
172 }
174 /* -bs or called as smtpd or in.smtpd */
175 static void
176 mode_smtp()
177 {
178 /* accept smtp message on stdin */
179 /* write responses to stderr. */
181 struct sockaddr_in saddr;
182 gchar *peername = NULL;
183 int dummy = sizeof(saddr);
185 conf.do_verbose = FALSE;
187 if (!conf.run_as_user) {
188 seteuid(conf.orig_uid);
189 setegid(conf.orig_gid);
190 }
192 DEBUG(5) debugf("accepting smtp message on stdin\n");
194 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
195 peername = g_strdup(inet_ntoa(saddr.sin_addr));
196 } else if (errno != ENOTSOCK)
197 exit(1);
199 smtp_in(stdin, stderr, peername, NULL);
200 }
202 /* default mode if address args or -t is specified, or called as rmail */
203 static void
204 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
205 {
206 /* accept message on stdin */
207 accept_error err;
208 message *msg = create_message();
209 gint i;
210 pid_t pid;
212 if (return_path && !is_privileged_user(conf.orig_uid)) {
213 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
214 exit(1);
215 }
217 if (!conf.run_as_user) {
218 seteuid(conf.orig_uid);
219 setegid(conf.orig_gid);
220 }
222 DEBUG(5) debugf("accepting message on stdin\n");
224 msg->received_prot = PROT_LOCAL;
226 /* warn if -t option and cmdline addr args */
227 if (addr_cnt && (accept_flags & ACC_RCPT_FROM_HEAD)) {
228 logwrite(LOG_ALERT, "command line address arguments are now *added* to the mail header\\\n");
229 logwrite(LOG_ALERT, " recipient addresses (instead of substracted) when -t is given.\\\n");
230 logwrite(LOG_ALERT, " this changed with version 0.3.1\n");
231 }
233 for (i = 0; i < addr_cnt; i++) {
234 if (addresses[i][0] == '|') {
235 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
236 /* should we better ignore this one addr? */
237 exit(1);
238 }
239 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
240 }
242 /* -f option */
243 msg->return_path = return_path;
245 /* -F option */
246 msg->full_sender_name = full_sender_name;
248 err = accept_message(stdin, msg, accept_flags);
250 switch (err) {
251 case AERR_OK:
252 /* to continue; all other cases exit */
253 break;
254 case AERR_EOF:
255 fprintf(stderr, "unexpected EOF.\n");
256 exit(1);
257 case AERR_NORCPT:
258 fprintf(stderr, "no recipients.\n");
259 exit(1);
260 case AERR_SIZE:
261 fprintf(stderr, "max message size exceeded.\n");
262 exit(1);
263 default:
264 /* should never happen: */
265 fprintf(stderr, "Unknown error (%d)\r\n", err);
266 exit(1);
267 }
269 if (!spool_write(msg, TRUE)) {
270 fprintf(stderr, "Could not write spool file\n");
271 exit(1);
272 }
274 /* here the mail is queued and thus in our responsibility */
275 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
277 if (conf.do_queue) {
278 /* we're finished as we only need to queue it */
279 return;
280 }
282 /* deliver at once */
283 if ((pid = fork()) < 0) {
284 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
285 } else if (pid == 0) {
286 conf.do_verbose = FALSE;
287 fclose(stdin);
288 fclose(stdout);
289 fclose(stderr);
290 if (deliver(msg)) {
291 exit(0);
292 } else {
293 /*
294 TODO:
295 Should we really fail here? Because the mail is queued
296 already. If we fail the client might submit it again.
297 If at-once-delivery is seen as an additional best-effort
298 service, then we should still exit successful here.
299 */
300 exit(1);
301 }
302 }
303 }
305 /*
306 if -Mrm is given
308 currently only the `rm' command is supported
309 until this changes, we don't need any facility for further commands
310 return success if at least one message had been deleted
311 */
312 static int
313 manipulate_queue(char* cmd, char* id[])
314 {
315 gboolean ok = FALSE;
317 if (strcmp(cmd, "rm") != 0) {
318 fprintf(stderr, "unknown command %s\n", cmd);
319 return FALSE;
320 }
322 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
324 /* privileged users may delete any mail */
325 if (is_privileged_user(conf.orig_uid)) {
326 for (; *id; id++) {
327 fprintf(stderr, "id: %s\n", *id);
328 if (queue_delete(*id)) {
329 ok = TRUE;
330 }
331 }
332 return ok;
333 }
335 struct passwd *pw = getpwuid(conf.orig_uid);
336 if (!pw) {
337 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
338 conf.orig_uid, strerror(errno));
339 return FALSE;
340 }
342 /* non-privileged users may only delete their own messages */
343 for (; *id; id++) {
344 message *msg = msg_spool_read(*id, FALSE);
346 fprintf(stderr, "id: %s\n", *id);
348 if (!msg->ident) {
349 fprintf(stderr, "message %s does not have an ident\n", *id);
350 continue;
351 }
352 if (strcmp(pw->pw_name, msg->ident) != 0) {
353 fprintf(stderr, "you do not own message id %s\n", *id);
354 continue;
355 }
357 if ( (msg->received_host || (msg->received_prot != PROT_LOCAL))
358 #ifdef ENABLE_IDENT
359 && !is_in_netlist(msg->received_host, conf.ident_trusted_nets)
360 #endif
361 ) {
362 fprintf(stderr, "message %s was not received locally or from a trusted network\n", *id);
363 continue;
364 }
366 ok = queue_delete(*id);
367 }
368 return ok;
369 }
371 /* -qo, -q (without argument), or called as runq */
372 /* TODO: are -qo and -q exclusively or not?
373 And how is this related to being a daemon? */
374 static int
375 run_queue(gboolean do_runq, gboolean do_runq_online, char* route_name)
376 {
377 int ret;
379 /* queue runs */
380 set_identity(conf.orig_uid, "queue run");
382 if (do_runq) {
383 ret = queue_run();
384 }
386 if (do_runq_online) {
387 if (route_name) {
388 conf.online_detect = g_strdup("argument");
389 set_online_name(route_name);
390 }
391 ret = queue_run_online();
392 }
393 return ret;
394 }
396 /* -bV or default mode if neither addr arg nor -t */
397 static void
398 mode_version(void)
399 {
400 gchar *with_resolver = "";
401 gchar *with_auth = "";
402 gchar *with_ident = "";
404 #ifdef ENABLE_RESOLVER
405 with_resolver = " +resolver";
406 #endif
407 #ifdef ENABLE_AUTH
408 with_auth = " +auth";
409 #endif
410 #ifdef ENABLE_IDENT
411 with_ident = " +ident";
412 #endif
414 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
415 }
417 int
418 main(int argc, char *argv[])
419 {
420 gchar *progname;
421 char* opt;
422 gint arg;
424 mta_mode mta_mode = MODE_ACCEPT;
425 gboolean do_listen = FALSE;
426 gboolean do_runq = FALSE;
427 gboolean do_runq_online = FALSE;
428 gboolean do_queue = FALSE;
429 gint queue_interval = 0;
430 gchar *M_cmd = NULL;
431 gboolean opt_t = FALSE;
432 gboolean opt_i = FALSE;
433 gchar *conf_file = CONF_FILE;
434 gchar *route_name = NULL;
435 gchar *f_address = NULL;
436 address *return_path = NULL; /* may be changed by -f option */
437 gchar *full_sender_name = NULL;
438 gboolean do_verbose = FALSE;
439 gint debug_level = -1;
441 /* strip the path part */
442 progname = strrchr(argv[0], '/');
443 progname = (progname) ? progname+1 : argv[0];
445 if (strcmp(progname, "mailq") == 0) {
446 mta_mode = MODE_LIST;
447 } else if (strcmp(progname, "mailrm") == 0) {
448 mta_mode = MODE_MCMD;
449 M_cmd = "rm";
450 } else if (strcmp(progname, "runq") == 0) {
451 mta_mode = MODE_RUNQUEUE;
452 do_runq = TRUE;
453 } else if (strcmp(progname, "rmail") == 0) {
454 /* the `rmail' alias should probably be removed now
455 that we have the rmail script. But let's keep it
456 for some while for compatibility. 2010-06-19 */
457 mta_mode = MODE_ACCEPT;
458 opt_i = TRUE;
459 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
460 mta_mode = MODE_SMTP;
461 }
463 /* parse cmd line */
464 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
465 opt = argv[arg] + 1; /* points to the char after the dash */
467 if (strcmp(opt, "-") == 0) {
468 /* everything after `--' are address arguments */
469 arg++;
470 break;
472 } else if (strcmp(opt, "bm") == 0) {
473 mta_mode = MODE_ACCEPT;
475 } else if (strcmp(opt, "bd") == 0) {
476 do_listen = TRUE;
477 mta_mode = MODE_DAEMON;
479 } else if (strcmp(opt, "bi") == 0) {
480 /* ignored */
481 mta_mode = MODE_BI;
483 } else if (strcmp(opt, "bs") == 0) {
484 mta_mode = MODE_SMTP;
486 } else if (strcmp(opt, "bp") == 0) {
487 mta_mode = MODE_LIST;
489 } else if (strcmp(opt, "bV") == 0) {
490 mta_mode = MODE_VERSION;
492 } else if (strncmp(opt, "B", 1) == 0) {
493 /* we ignore this and throw the argument away */
494 get_optarg(argv, &arg, opt+1);
496 } else if (strncmp(opt, "C", 1) == 0) {
497 conf_file = get_optarg(argv, &arg, opt+1);
498 if (!conf_file) {
499 fprintf(stderr, "-C requires a filename as argument.\n");
500 exit(1);
501 }
503 } else if (strncmp(opt, "d", 1) == 0) {
504 if (getuid() != 0) {
505 fprintf(stderr, "only root may set the debug level.\n");
506 exit(1);
507 }
508 char *lvl = get_optarg(argv, &arg, opt+1);
509 if (!lvl) {
510 fprintf(stderr, "-d requires a number argument.\n");
511 exit(1);
512 }
513 debug_level = atoi(lvl);
515 } else if (strncmp(opt, "f", 1) == 0) {
516 /* set return path */
517 gchar *address = get_optarg(argv, &arg, opt+1);
518 if (!address) {
519 fprintf(stderr, "-f requires an address argument\n");
520 exit(1);
521 }
522 f_address = g_strdup(address);
524 } else if (strncmp(opt, "F", 1) == 0) {
525 full_sender_name = get_optarg(argv, &arg, opt+1);
526 if (!full_sender_name) {
527 fprintf(stderr, "-F requires a name argument\n");
528 exit(1);
529 }
531 } else if (strcmp(opt, "i") == 0) {
532 opt_i = TRUE;
534 } else if (strcmp(opt, "m") == 0) {
535 /* ignore -m (me too) switch (see man page) */
537 } else if (strcmp(opt, "Mrm") == 0) {
538 mta_mode = MODE_MCMD;
539 M_cmd = "rm";
541 } else if (strcmp(opt, "odq") == 0) {
542 do_queue = TRUE;
544 } else if (strcmp(opt, "oi") == 0) {
545 opt_i = TRUE;
547 } else if (strncmp(opt, "o", 1) == 0) {
548 /* ignore all other -oXXX options */
550 } else if (strncmp(opt, "qo", 2) == 0) {
551 /* must be before the `q' check */
552 mta_mode = MODE_RUNQUEUE;
553 do_runq = FALSE;
554 do_runq_online = TRUE;
555 /* can be NULL, then we use online detection method */
556 route_name = get_optarg(argv, &arg, opt+2);
558 } else if (strncmp(opt, "q", 1) == 0) {
559 /* must be after the `qo' check */
560 gchar *optarg;
562 do_runq = TRUE;
563 mta_mode = MODE_RUNQUEUE;
564 optarg = get_optarg(argv, &arg, opt+1);
565 if (optarg) {
566 /* not just one single queue run but regular runs */
567 mta_mode = MODE_DAEMON;
568 queue_interval = time_interval(optarg);
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==MODE_ACCEPT && 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 mta_mode = MODE_VERSION;
591 }
593 if (mta_mode == MODE_VERSION) {
594 mode_version();
595 exit(0);
596 }
598 /* initialize random generator */
599 srand(time(NULL));
600 /* ignore SIGPIPE signal */
601 signal(SIGPIPE, SIG_IGN);
603 /* close all possibly open file descriptors, except std{in,out,err} */
604 {
605 int i, max_fd = sysconf(_SC_OPEN_MAX);
607 if (max_fd <= 0) {
608 max_fd = 64;
609 }
610 for (i=3; i<max_fd; i++) {
611 close(i);
612 }
613 }
615 init_conf();
617 /* if we are not privileged, and the config file was changed we
618 implicetely set the the run_as_user flag and give up all
619 privileges.
621 So it is possible for a user to run his own daemon without
622 breaking security.
623 */
624 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
625 conf.run_as_user = TRUE;
626 seteuid(conf.orig_uid);
627 setegid(conf.orig_gid);
628 setuid(conf.orig_uid);
629 setgid(conf.orig_gid);
630 }
632 conf.log_dir = LOG_DIR;
633 logopen();
634 if (!read_conf(conf_file)) {
635 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
636 exit(5);
637 }
638 logclose();
640 if (do_queue) {
641 conf.do_queue = TRUE;
642 }
643 if (do_verbose) {
644 conf.do_verbose = TRUE;
645 }
646 if (debug_level >= 0) { /* if >= 0, it was given by argument */
647 conf.debug_level = debug_level;
648 }
650 /* It appears that changing to / ensures that we are never in
651 a directory which we cannot access. This situation could be
652 possible after changing identity.
653 Maybe we should only change to / if we not run as user, to
654 allow relative paths for log files in test setups for
655 instance.
656 */
657 chdir("/");
659 if (!conf.run_as_user) {
660 if (setgid(0) != 0) {
661 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
662 exit(1);
663 }
664 if (setuid(0) != 0) {
665 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
666 exit(1);
667 }
668 }
670 if (!logopen()) {
671 fprintf(stderr, "could not open log file\n");
672 exit(1);
673 }
675 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
677 DEBUG(5) {
678 gchar **str = argv;
679 debugf("args: \n");
680 while (*str) {
681 debugf("%s \n", *str);
682 str++;
683 }
684 }
685 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
687 if (f_address) {
688 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
689 g_free(f_address);
690 if (!return_path) {
691 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
692 exit(1);
693 }
694 }
696 switch (mta_mode) {
697 case MODE_DAEMON:
698 mode_daemon(do_listen, queue_interval, argv);
699 break;
701 case MODE_RUNQUEUE:
702 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
703 break;
705 case MODE_SMTP:
706 mode_smtp();
707 break;
709 case MODE_LIST:
710 queue_list();
711 break;
713 case MODE_BI:
714 exit(0);
715 break; /* well... */
717 case MODE_MCMD:
718 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
719 break;
721 case MODE_ACCEPT:
722 {
723 guint accept_flags = (opt_t ? ACC_RCPT_FROM_HEAD : 0)
724 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
725 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
726 exit(0);
727 }
728 break;
730 default:
731 fprintf(stderr, "unknown mode: %d\n", mta_mode);
732 break;
733 }
735 logclose();
737 exit(0);
738 }