masqmail
view src/masqmail.c @ 262:fc1c6425c024
s/EXIT_SUCCESS/0/ && s/EXIT_FAILURE/1/
The constants are all to bulky.
We should have different, meaningful exit codes anyway.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Thu, 02 Dec 2010 17:11:25 -0300 |
parents | 0afe18a9ee03 |
children | e9e73505ab2c |
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 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 static void
175 mode_smtp()
176 {
177 /* accept smtp message on stdin */
178 /* write responses to stderr. */
180 struct sockaddr_in saddr;
181 gchar *peername = NULL;
182 int dummy = sizeof(saddr);
184 conf.do_verbose = FALSE;
186 if (!conf.run_as_user) {
187 seteuid(conf.orig_uid);
188 setegid(conf.orig_gid);
189 }
191 DEBUG(5) debugf("accepting smtp message on stdin\n");
193 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
194 peername = g_strdup(inet_ntoa(saddr.sin_addr));
195 } else if (errno != ENOTSOCK)
196 exit(1);
198 smtp_in(stdin, stderr, peername, NULL);
199 }
201 static void
202 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
203 {
204 /* accept message on stdin */
205 accept_error err;
206 message *msg = create_message();
207 gint i;
209 if (return_path && !is_privileged_user(conf.orig_uid)) {
210 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
211 exit(1);
212 }
214 if (!conf.run_as_user) {
215 seteuid(conf.orig_uid);
216 setegid(conf.orig_gid);
217 }
219 DEBUG(5) debugf("accepting message on stdin\n");
221 msg->received_prot = PROT_LOCAL;
222 for (i = 0; i < addr_cnt; i++) {
223 if (addresses[i][0] != '|')
224 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
225 else {
226 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
227 exit(1);
228 }
229 }
231 /* -f option */
232 msg->return_path = return_path;
234 /* -F option */
235 msg->full_sender_name = full_sender_name;
237 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
238 if (spool_write(msg, TRUE)) {
239 pid_t pid;
240 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
242 if (!conf.do_queue) {
243 if ((pid = fork()) == 0) {
244 conf.do_verbose = FALSE;
245 fclose(stdin);
246 fclose(stdout);
247 fclose(stderr);
248 if (deliver(msg)) {
249 exit(0);
250 } else
251 exit(1);
252 } else if (pid < 0) {
253 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
254 }
255 }
256 } else {
257 fprintf(stderr, "Could not write spool file\n");
258 exit(1);
259 }
260 } else {
261 switch (err) {
262 case AERR_EOF:
263 fprintf(stderr, "unexpected EOF.\n");
264 exit(1);
265 case AERR_NORCPT:
266 fprintf(stderr, "no recipients.\n");
267 exit(1);
268 case AERR_SIZE:
269 fprintf(stderr, "max message size exceeded.\n");
270 exit(1);
271 default:
272 /* should never happen: */
273 fprintf(stderr, "Unknown error (%d)\r\n", err);
274 exit(1);
275 }
276 exit(1);
277 }
278 }
280 /*
281 currently only the `rm' command is supported
282 until this changes, we don't need any facility for further commands
283 return success if at least one message had been deleted
284 */
285 static int
286 manipulate_queue(char* cmd, char* id[])
287 {
288 gboolean ok = FALSE;
290 if (strcmp(cmd, "rm") != 0) {
291 fprintf(stderr, "unknown command %s\n", cmd);
292 return FALSE;
293 }
295 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
297 /* privileged users may delete any mail */
298 if (is_privileged_user(conf.orig_uid)) {
299 for (; *id; id++) {
300 fprintf(stderr, "id: %s\n", *id);
301 if (queue_delete(*id)) {
302 ok = TRUE;
303 }
304 }
305 return ok;
306 }
308 struct passwd *pw = getpwuid(conf.orig_uid);
309 if (!pw) {
310 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
311 conf.orig_uid, strerror(errno));
312 return FALSE;
313 }
315 /* non-privileged users may only delete their own messages */
316 for (; *id; id++) {
317 message *msg = msg_spool_read(*id, FALSE);
319 fprintf(stderr, "id: %s\n", *id);
321 if (!msg->ident) {
322 fprintf(stderr, "message %s does not have an ident\n", *id);
323 continue;
324 }
325 if (strcmp(pw->pw_name, msg->ident) != 0) {
326 fprintf(stderr, "you do not own message id %s\n", *id);
327 continue;
328 }
330 if ( (msg->received_host || (msg->received_prot != PROT_LOCAL))
331 #ifdef ENABLE_IDENT
332 && !is_in_netlist(msg->received_host, conf.ident_trusted_nets)
333 #endif
334 ) {
335 fprintf(stderr, "message %s was not received locally or from a trusted network\n", *id);
336 continue;
337 }
339 ok = queue_delete(*id);
340 }
341 return ok;
342 }
344 static int
345 run_queue(gboolean do_runq, gboolean do_runq_online, char* route_name)
346 {
347 int ret;
349 /* queue runs */
350 set_identity(conf.orig_uid, "queue run");
352 if (do_runq) {
353 ret = queue_run();
354 }
356 if (do_runq_online) {
357 if (route_name) {
358 conf.online_detect = g_strdup("argument");
359 set_online_name(route_name);
360 }
361 ret = queue_run_online();
362 }
363 return ret;
364 }
366 static void
367 mode_version(void)
368 {
369 gchar *with_resolver = "";
370 gchar *with_auth = "";
371 gchar *with_ident = "";
373 #ifdef ENABLE_RESOLVER
374 with_resolver = " +resolver";
375 #endif
376 #ifdef ENABLE_AUTH
377 with_auth = " +auth";
378 #endif
379 #ifdef ENABLE_IDENT
380 with_ident = " +ident";
381 #endif
383 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
384 }
386 int
387 main(int argc, char *argv[])
388 {
389 gchar *progname;
390 char* opt;
391 gint arg;
393 mta_mode mta_mode = MODE_NONE;
394 gboolean do_listen = FALSE;
395 gboolean do_runq = FALSE;
396 gboolean do_runq_online = FALSE;
397 gboolean do_queue = FALSE;
398 gint queue_interval = 0;
399 gchar *M_cmd = NULL;
400 gboolean opt_t = FALSE;
401 gboolean opt_i = FALSE;
402 gint exit_code = 0;
403 gchar *conf_file = CONF_FILE;
404 gchar *route_name = NULL;
405 gchar *f_address = NULL;
406 address *return_path = NULL; /* may be changed by -f option */
407 gchar *full_sender_name = NULL;
408 gboolean do_verbose = FALSE;
409 gint debug_level = -1;
411 /* strip the path part */
412 progname = strrchr(argv[0], '/');
413 progname = (progname) ? progname+1 : argv[0];
415 if (strcmp(progname, "mailq") == 0) {
416 mta_mode = MODE_LIST;
417 } else if (strcmp(progname, "mailrm") == 0) {
418 mta_mode = MODE_MCMD;
419 M_cmd = "rm";
420 } else if (strcmp(progname, "runq") == 0) {
421 mta_mode = MODE_RUNQUEUE;
422 do_runq = TRUE;
423 } else if (strcmp(progname, "rmail") == 0) {
424 /* the `rmail' alias should probably be removed now
425 that we have the rmail script. But let's keep it
426 for some while for compatibility. 2010-06-19 */
427 mta_mode = MODE_ACCEPT;
428 opt_i = TRUE;
429 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
430 mta_mode = MODE_SMTP;
431 }
433 /* parse cmd line */
434 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
435 opt = argv[arg] + 1; /* points to the char after the dash */
437 if (strcmp(opt, "-") == 0) {
438 /* everything after `--' are address arguments */
439 arg++;
440 break;
442 } else if (strcmp(opt, "bd") == 0) {
443 do_listen = TRUE;
444 mta_mode = MODE_DAEMON;
446 } else if (strcmp(opt, "bi") == 0) {
447 /* ignored */
448 mta_mode = MODE_BI;
450 } else if (strcmp(opt, "bs") == 0) {
451 mta_mode = MODE_SMTP;
453 } else if (strcmp(opt, "bp") == 0) {
454 mta_mode = MODE_LIST;
456 } else if (strcmp(opt, "bV") == 0) {
457 mta_mode = MODE_VERSION;
459 } else if (strncmp(opt, "B", 1) == 0) {
460 /* we ignore this and throw the argument away */
461 get_optarg(argv, &arg, opt+1);
463 } else if (strncmp(opt, "C", 1) == 0) {
464 conf_file = get_optarg(argv, &arg, opt+1);
465 if (!conf_file) {
466 fprintf(stderr, "-C requires a filename as argument.\n");
467 exit(1);
468 }
470 } else if (strncmp(opt, "d", 1) == 0) {
471 if (getuid() != 0) {
472 fprintf(stderr, "only root may set the debug level.\n");
473 exit(1);
474 }
475 char *lvl = get_optarg(argv, &arg, opt+1);
476 if (!lvl) {
477 fprintf(stderr, "-d requires a number argument.\n");
478 exit(1);
479 }
480 debug_level = atoi(lvl);
482 } else if (strncmp(opt, "f", 1) == 0) {
483 /* set return path */
484 gchar *address = get_optarg(argv, &arg, opt+1);
485 if (!address) {
486 fprintf(stderr, "-f requires an address argument\n");
487 exit(1);
488 }
489 f_address = g_strdup(address);
491 } else if (strncmp(opt, "F", 1) == 0) {
492 full_sender_name = get_optarg(argv, &arg, opt+1);
493 if (!full_sender_name) {
494 fprintf(stderr, "-F requires a name argument\n");
495 exit(1);
496 }
498 } else if (strcmp(opt, "i") == 0) {
499 opt_i = TRUE;
501 } else if (strcmp(opt, "m") == 0) {
502 /* ignore -m (me too) switch (see man page) */
504 } else if (strcmp(opt, "Mrm") == 0) {
505 mta_mode = MODE_MCMD;
506 M_cmd = "rm";
508 } else if (strcmp(opt, "odq") == 0) {
509 do_queue = TRUE;
511 } else if (strcmp(opt, "oi") == 0) {
512 opt_i = TRUE;
514 } else if (strncmp(opt, "o", 1) == 0) {
515 /* ignore all other -oXXX options */
517 } else if (strncmp(opt, "qo", 2) == 0) {
518 mta_mode = MODE_RUNQUEUE;
519 do_runq = FALSE;
520 do_runq_online = TRUE;
521 /* can be NULL, then we use online detection method */
522 route_name = get_optarg(argv, &arg, opt+2);
524 } else if (strncmp(opt, "q", 1) == 0) {
525 /* must be after the `qo' check */
526 gchar *optarg;
528 do_runq = TRUE;
529 mta_mode = MODE_RUNQUEUE;
530 optarg = get_optarg(argv, &arg, opt+1);
531 if (optarg) {
532 /* not just one single queue run but regular runs */
533 mta_mode = MODE_DAEMON;
534 queue_interval = time_interval(optarg);
535 }
537 } else if (strcmp(opt, "t") == 0) {
538 opt_t = TRUE;
540 } else if (strcmp(opt, "v") == 0) {
541 do_verbose = TRUE;
543 } else {
544 fprintf(stderr, "unrecognized option `-%s'\n", opt);
545 exit(1);
546 }
547 }
549 if (!mta_mode) {
550 mta_mode = (arg<argc || opt_t) ? MODE_ACCEPT : MODE_VERSION;
551 }
553 if (mta_mode == MODE_VERSION) {
554 mode_version();
555 exit(0);
556 }
558 /* initialize random generator */
559 srand(time(NULL));
560 /* ignore SIGPIPE signal */
561 signal(SIGPIPE, SIG_IGN);
563 /* close all possibly open file descriptors, except std{in,out,err} */
564 {
565 int i, max_fd = sysconf(_SC_OPEN_MAX);
567 if (max_fd <= 0) {
568 max_fd = 64;
569 }
570 for (i=3; i<max_fd; i++) {
571 close(i);
572 }
573 }
575 init_conf();
577 /* if we are not privileged, and the config file was changed we
578 implicetely set the the run_as_user flag and give up all
579 privileges.
581 So it is possible for a user to run his own daemon without
582 breaking security.
583 */
584 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
585 conf.run_as_user = TRUE;
586 seteuid(conf.orig_uid);
587 setegid(conf.orig_gid);
588 setuid(conf.orig_uid);
589 setgid(conf.orig_gid);
590 }
592 conf.log_dir = LOG_DIR;
593 logopen();
594 if (!read_conf(conf_file)) {
595 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
596 exit(5);
597 }
598 logclose();
600 if (do_queue) {
601 conf.do_queue = TRUE;
602 }
603 if (do_verbose) {
604 conf.do_verbose = TRUE;
605 }
606 if (debug_level >= 0) { /* if >= 0, it was given by argument */
607 conf.debug_level = debug_level;
608 }
610 /* It appears that changing to / ensures that we are never in
611 a directory which we cannot access. This situation could be
612 possible after changing identity.
613 Maybe we should only change to / if we not run as user, to
614 allow relative paths for log files in test setups for
615 instance.
616 */
617 chdir("/");
619 if (!conf.run_as_user) {
620 if (setgid(0) != 0) {
621 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
622 exit(1);
623 }
624 if (setuid(0) != 0) {
625 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
626 exit(1);
627 }
628 }
630 if (!logopen()) {
631 fprintf(stderr, "could not open log file\n");
632 exit(1);
633 }
635 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
637 DEBUG(5) {
638 gchar **str = argv;
639 debugf("args: \n");
640 while (*str) {
641 debugf("%s \n", *str);
642 str++;
643 }
644 }
645 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
647 if (f_address) {
648 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
649 g_free(f_address);
650 if (!return_path) {
651 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
652 exit(1);
653 }
654 }
656 switch (mta_mode) {
657 case MODE_DAEMON:
658 mode_daemon(do_listen, queue_interval, argv);
659 break;
661 case MODE_RUNQUEUE:
662 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
663 break;
665 case MODE_SMTP:
666 mode_smtp();
667 break;
669 case MODE_LIST:
670 queue_list();
671 break;
673 case MODE_BI:
674 exit(0);
675 break; /* well... */
677 case MODE_MCMD:
678 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
679 break;
681 case MODE_ACCEPT:
682 {
683 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
684 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
685 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
686 exit(0);
687 }
688 break;
690 default:
691 fprintf(stderr, "unknown mode: %d\n", mta_mode);
692 break;
693 }
695 logclose();
697 exit(exit_code);
698 }