masqmail

view src/masqmail.c @ 264:1e5e457dea18

comments and a small refactoring
author markus schnalke <meillo@marmaro.de>
date Fri, 03 Dec 2010 10:43:38 -0300
parents e9e73505ab2c
children ab39047ffe44
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_NONE = 0, /* for being able to check if a mode was defined */
42 MODE_ACCEPT, /* accept message on stdin */
43 MODE_DAEMON, /* run as daemon */
44 MODE_RUNQUEUE, /* single queue run, online or offline */
45 MODE_SMTP, /* accept SMTP on stdin */
46 MODE_LIST, /* list queue */
47 MODE_MCMD, /* do queue manipulation */
48 MODE_VERSION, /* show version */
49 MODE_BI, /* fake ;-) */
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 /*
95 argv: the original argv
96 argp: number of arg (may get modified!)
97 cp: pointing to the char after the option
98 e.g. `-d 6' `-d6'
99 ^ ^
100 */
101 gchar*
102 get_optarg(char* argv[], gint* argp, char* cp)
103 {
104 if (*cp) {
105 /* this kind: -xval */
106 return cp;
107 }
108 cp = argv[*argp+1];
109 if (cp && (*cp != '-')) {
110 /* this kind: -x val */
111 (*argp)++;
112 return cp;
113 }
114 return NULL;
115 }
117 gboolean
118 write_pidfile(gchar * name)
119 {
120 FILE *fptr;
122 if ((fptr = fopen(name, "wt"))) {
123 fprintf(fptr, "%d\n", getpid());
124 fclose(fptr);
125 pidfile = strdup(name);
126 return TRUE;
127 }
128 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
129 return FALSE;
130 }
132 /* on -bd or if -q has an argument */
133 static void
134 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
135 {
136 guint pid;
138 /* daemon */
139 if (!conf.run_as_user) {
140 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
141 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
142 exit(1);
143 }
144 }
146 /* reparent to init only if init is not already the parent */
147 if (getppid() != 1) {
148 if ((pid = fork()) > 0) {
149 exit(0);
150 } else if (pid < 0) {
151 logwrite(LOG_ALERT, "could not fork!\n");
152 exit(1);
153 }
154 }
156 signal(SIGTERM, sigterm_handler);
157 write_pidfile(PIDFILEDIR "/masqmail.pid");
159 conf.do_verbose = FALSE;
161 /* closing and reopening the log ensures that it is open afterwards
162 because it is possible that the log is assigned to fd 1 and gets
163 thus closes by fclose(stdout). Similar for the debugfile.
164 */
165 logclose();
166 fclose(stdin);
167 fclose(stdout);
168 fclose(stderr);
169 logopen();
171 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
172 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
173 }
175 /* -bs or called as smtpd or in.smtpd */
176 static void
177 mode_smtp()
178 {
179 /* accept smtp message on stdin */
180 /* write responses to stderr. */
182 struct sockaddr_in saddr;
183 gchar *peername = NULL;
184 int dummy = sizeof(saddr);
186 conf.do_verbose = FALSE;
188 if (!conf.run_as_user) {
189 seteuid(conf.orig_uid);
190 setegid(conf.orig_gid);
191 }
193 DEBUG(5) debugf("accepting smtp message on stdin\n");
195 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
196 peername = g_strdup(inet_ntoa(saddr.sin_addr));
197 } else if (errno != ENOTSOCK)
198 exit(1);
200 smtp_in(stdin, stderr, peername, NULL);
201 }
203 /* default mode if address args or -t is specified, or called as rmail */
204 static void
205 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
206 {
207 /* accept message on stdin */
208 accept_error err;
209 message *msg = create_message();
210 gint i;
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;
225 for (i = 0; i < addr_cnt; i++) {
226 if (addresses[i][0] == '|')
227 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
228 exit(1);
229 }
230 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
231 }
233 /* -f option */
234 msg->return_path = return_path;
236 /* -F option */
237 msg->full_sender_name = full_sender_name;
239 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
240 if (spool_write(msg, TRUE)) {
241 pid_t pid;
242 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
244 if (!conf.do_queue) {
245 if ((pid = fork()) == 0) {
246 conf.do_verbose = FALSE;
247 fclose(stdin);
248 fclose(stdout);
249 fclose(stderr);
250 if (deliver(msg)) {
251 exit(0);
252 } else
253 exit(1);
254 } else if (pid < 0) {
255 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
256 }
257 }
258 } else {
259 fprintf(stderr, "Could not write spool file\n");
260 exit(1);
261 }
262 } else {
263 switch (err) {
264 case AERR_EOF:
265 fprintf(stderr, "unexpected EOF.\n");
266 exit(1);
267 case AERR_NORCPT:
268 fprintf(stderr, "no recipients.\n");
269 exit(1);
270 case AERR_SIZE:
271 fprintf(stderr, "max message size exceeded.\n");
272 exit(1);
273 default:
274 /* should never happen: */
275 fprintf(stderr, "Unknown error (%d)\r\n", err);
276 exit(1);
277 }
278 exit(1);
279 }
280 }
282 /*
283 if -Mrm is given
285 currently only the `rm' command is supported
286 until this changes, we don't need any facility for further commands
287 return success if at least one message had been deleted
288 */
289 static int
290 manipulate_queue(char* cmd, char* id[])
291 {
292 gboolean ok = FALSE;
294 if (strcmp(cmd, "rm") != 0) {
295 fprintf(stderr, "unknown command %s\n", cmd);
296 return FALSE;
297 }
299 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
301 /* privileged users may delete any mail */
302 if (is_privileged_user(conf.orig_uid)) {
303 for (; *id; id++) {
304 fprintf(stderr, "id: %s\n", *id);
305 if (queue_delete(*id)) {
306 ok = TRUE;
307 }
308 }
309 return ok;
310 }
312 struct passwd *pw = getpwuid(conf.orig_uid);
313 if (!pw) {
314 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
315 conf.orig_uid, strerror(errno));
316 return FALSE;
317 }
319 /* non-privileged users may only delete their own messages */
320 for (; *id; id++) {
321 message *msg = msg_spool_read(*id, FALSE);
323 fprintf(stderr, "id: %s\n", *id);
325 if (!msg->ident) {
326 fprintf(stderr, "message %s does not have an ident\n", *id);
327 continue;
328 }
329 if (strcmp(pw->pw_name, msg->ident) != 0) {
330 fprintf(stderr, "you do not own message id %s\n", *id);
331 continue;
332 }
334 if ( (msg->received_host || (msg->received_prot != PROT_LOCAL))
335 #ifdef ENABLE_IDENT
336 && !is_in_netlist(msg->received_host, conf.ident_trusted_nets)
337 #endif
338 ) {
339 fprintf(stderr, "message %s was not received locally or from a trusted network\n", *id);
340 continue;
341 }
343 ok = queue_delete(*id);
344 }
345 return ok;
346 }
348 /* -qo, -q (without argument), or called as runq */
349 /* TODO: are -qo and -q exclusively or not?
350 And how is this related to being a daemon? */
351 static int
352 run_queue(gboolean do_runq, gboolean do_runq_online, char* route_name)
353 {
354 int ret;
356 /* queue runs */
357 set_identity(conf.orig_uid, "queue run");
359 if (do_runq) {
360 ret = queue_run();
361 }
363 if (do_runq_online) {
364 if (route_name) {
365 conf.online_detect = g_strdup("argument");
366 set_online_name(route_name);
367 }
368 ret = queue_run_online();
369 }
370 return ret;
371 }
373 /* -bV or default mode if neither addr arg nor -t */
374 static void
375 mode_version(void)
376 {
377 gchar *with_resolver = "";
378 gchar *with_auth = "";
379 gchar *with_ident = "";
381 #ifdef ENABLE_RESOLVER
382 with_resolver = " +resolver";
383 #endif
384 #ifdef ENABLE_AUTH
385 with_auth = " +auth";
386 #endif
387 #ifdef ENABLE_IDENT
388 with_ident = " +ident";
389 #endif
391 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
392 }
394 int
395 main(int argc, char *argv[])
396 {
397 gchar *progname;
398 char* opt;
399 gint arg;
401 mta_mode mta_mode = MODE_NONE;
402 gboolean do_listen = FALSE;
403 gboolean do_runq = FALSE;
404 gboolean do_runq_online = FALSE;
405 gboolean do_queue = FALSE;
406 gint queue_interval = 0;
407 gchar *M_cmd = NULL;
408 gboolean opt_t = FALSE;
409 gboolean opt_i = FALSE;
410 gchar *conf_file = CONF_FILE;
411 gchar *route_name = NULL;
412 gchar *f_address = NULL;
413 address *return_path = NULL; /* may be changed by -f option */
414 gchar *full_sender_name = NULL;
415 gboolean do_verbose = FALSE;
416 gint debug_level = -1;
418 /* strip the path part */
419 progname = strrchr(argv[0], '/');
420 progname = (progname) ? progname+1 : argv[0];
422 if (strcmp(progname, "mailq") == 0) {
423 mta_mode = MODE_LIST;
424 } else if (strcmp(progname, "mailrm") == 0) {
425 mta_mode = MODE_MCMD;
426 M_cmd = "rm";
427 } else if (strcmp(progname, "runq") == 0) {
428 mta_mode = MODE_RUNQUEUE;
429 do_runq = TRUE;
430 } else if (strcmp(progname, "rmail") == 0) {
431 /* the `rmail' alias should probably be removed now
432 that we have the rmail script. But let's keep it
433 for some while for compatibility. 2010-06-19 */
434 mta_mode = MODE_ACCEPT;
435 opt_i = TRUE;
436 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
437 mta_mode = MODE_SMTP;
438 }
440 /* parse cmd line */
441 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
442 opt = argv[arg] + 1; /* points to the char after the dash */
444 if (strcmp(opt, "-") == 0) {
445 /* everything after `--' are address arguments */
446 arg++;
447 break;
449 } else if (strcmp(opt, "bd") == 0) {
450 do_listen = TRUE;
451 mta_mode = MODE_DAEMON;
453 } else if (strcmp(opt, "bi") == 0) {
454 /* ignored */
455 mta_mode = MODE_BI;
457 } else if (strcmp(opt, "bs") == 0) {
458 mta_mode = MODE_SMTP;
460 } else if (strcmp(opt, "bp") == 0) {
461 mta_mode = MODE_LIST;
463 } else if (strcmp(opt, "bV") == 0) {
464 mta_mode = MODE_VERSION;
466 } else if (strncmp(opt, "B", 1) == 0) {
467 /* we ignore this and throw the argument away */
468 get_optarg(argv, &arg, opt+1);
470 } else if (strncmp(opt, "C", 1) == 0) {
471 conf_file = get_optarg(argv, &arg, opt+1);
472 if (!conf_file) {
473 fprintf(stderr, "-C requires a filename as argument.\n");
474 exit(1);
475 }
477 } else if (strncmp(opt, "d", 1) == 0) {
478 if (getuid() != 0) {
479 fprintf(stderr, "only root may set the debug level.\n");
480 exit(1);
481 }
482 char *lvl = get_optarg(argv, &arg, opt+1);
483 if (!lvl) {
484 fprintf(stderr, "-d requires a number argument.\n");
485 exit(1);
486 }
487 debug_level = atoi(lvl);
489 } else if (strncmp(opt, "f", 1) == 0) {
490 /* set return path */
491 gchar *address = get_optarg(argv, &arg, opt+1);
492 if (!address) {
493 fprintf(stderr, "-f requires an address argument\n");
494 exit(1);
495 }
496 f_address = g_strdup(address);
498 } else if (strncmp(opt, "F", 1) == 0) {
499 full_sender_name = get_optarg(argv, &arg, opt+1);
500 if (!full_sender_name) {
501 fprintf(stderr, "-F requires a name argument\n");
502 exit(1);
503 }
505 } else if (strcmp(opt, "i") == 0) {
506 opt_i = TRUE;
508 } else if (strcmp(opt, "m") == 0) {
509 /* ignore -m (me too) switch (see man page) */
511 } else if (strcmp(opt, "Mrm") == 0) {
512 mta_mode = MODE_MCMD;
513 M_cmd = "rm";
515 } else if (strcmp(opt, "odq") == 0) {
516 do_queue = TRUE;
518 } else if (strcmp(opt, "oi") == 0) {
519 opt_i = TRUE;
521 } else if (strncmp(opt, "o", 1) == 0) {
522 /* ignore all other -oXXX options */
524 } else if (strncmp(opt, "qo", 2) == 0) {
525 mta_mode = MODE_RUNQUEUE;
526 do_runq = FALSE;
527 do_runq_online = TRUE;
528 /* can be NULL, then we use online detection method */
529 route_name = get_optarg(argv, &arg, opt+2);
531 } else if (strncmp(opt, "q", 1) == 0) {
532 /* must be after the `qo' check */
533 gchar *optarg;
535 do_runq = TRUE;
536 mta_mode = MODE_RUNQUEUE;
537 optarg = get_optarg(argv, &arg, opt+1);
538 if (optarg) {
539 /* not just one single queue run but regular runs */
540 mta_mode = MODE_DAEMON;
541 queue_interval = time_interval(optarg);
542 }
544 } else if (strcmp(opt, "t") == 0) {
545 opt_t = TRUE;
547 } else if (strcmp(opt, "v") == 0) {
548 do_verbose = TRUE;
550 } else {
551 fprintf(stderr, "unrecognized option `-%s'\n", opt);
552 exit(1);
553 }
554 }
556 if (!mta_mode) {
557 mta_mode = (arg<argc || opt_t) ? MODE_ACCEPT : MODE_VERSION;
558 }
560 if (mta_mode == MODE_VERSION) {
561 mode_version();
562 exit(0);
563 }
565 /* initialize random generator */
566 srand(time(NULL));
567 /* ignore SIGPIPE signal */
568 signal(SIGPIPE, SIG_IGN);
570 /* close all possibly open file descriptors, except std{in,out,err} */
571 {
572 int i, max_fd = sysconf(_SC_OPEN_MAX);
574 if (max_fd <= 0) {
575 max_fd = 64;
576 }
577 for (i=3; i<max_fd; i++) {
578 close(i);
579 }
580 }
582 init_conf();
584 /* if we are not privileged, and the config file was changed we
585 implicetely set the the run_as_user flag and give up all
586 privileges.
588 So it is possible for a user to run his own daemon without
589 breaking security.
590 */
591 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
592 conf.run_as_user = TRUE;
593 seteuid(conf.orig_uid);
594 setegid(conf.orig_gid);
595 setuid(conf.orig_uid);
596 setgid(conf.orig_gid);
597 }
599 conf.log_dir = LOG_DIR;
600 logopen();
601 if (!read_conf(conf_file)) {
602 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
603 exit(5);
604 }
605 logclose();
607 if (do_queue) {
608 conf.do_queue = TRUE;
609 }
610 if (do_verbose) {
611 conf.do_verbose = TRUE;
612 }
613 if (debug_level >= 0) { /* if >= 0, it was given by argument */
614 conf.debug_level = debug_level;
615 }
617 /* It appears that changing to / ensures that we are never in
618 a directory which we cannot access. This situation could be
619 possible after changing identity.
620 Maybe we should only change to / if we not run as user, to
621 allow relative paths for log files in test setups for
622 instance.
623 */
624 chdir("/");
626 if (!conf.run_as_user) {
627 if (setgid(0) != 0) {
628 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
629 exit(1);
630 }
631 if (setuid(0) != 0) {
632 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
633 exit(1);
634 }
635 }
637 if (!logopen()) {
638 fprintf(stderr, "could not open log file\n");
639 exit(1);
640 }
642 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
644 DEBUG(5) {
645 gchar **str = argv;
646 debugf("args: \n");
647 while (*str) {
648 debugf("%s \n", *str);
649 str++;
650 }
651 }
652 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
654 if (f_address) {
655 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
656 g_free(f_address);
657 if (!return_path) {
658 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
659 exit(1);
660 }
661 }
663 switch (mta_mode) {
664 case MODE_DAEMON:
665 mode_daemon(do_listen, queue_interval, argv);
666 break;
668 case MODE_RUNQUEUE:
669 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
670 break;
672 case MODE_SMTP:
673 mode_smtp();
674 break;
676 case MODE_LIST:
677 queue_list();
678 break;
680 case MODE_BI:
681 exit(0);
682 break; /* well... */
684 case MODE_MCMD:
685 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
686 break;
688 case MODE_ACCEPT:
689 {
690 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
691 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
692 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
693 exit(0);
694 }
695 break;
697 default:
698 fprintf(stderr, "unknown mode: %d\n", mta_mode);
699 break;
700 }
702 logclose();
704 exit(0);
705 }