masqmail

view src/masqmail.c @ 263:e9e73505ab2c

removed now obsolete exit_code
author markus schnalke <meillo@marmaro.de>
date Thu, 02 Dec 2010 17:13:03 -0300
parents fc1c6425c024
children 1e5e457dea18
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 gchar *conf_file = CONF_FILE;
403 gchar *route_name = NULL;
404 gchar *f_address = NULL;
405 address *return_path = NULL; /* may be changed by -f option */
406 gchar *full_sender_name = NULL;
407 gboolean do_verbose = FALSE;
408 gint debug_level = -1;
410 /* strip the path part */
411 progname = strrchr(argv[0], '/');
412 progname = (progname) ? progname+1 : argv[0];
414 if (strcmp(progname, "mailq") == 0) {
415 mta_mode = MODE_LIST;
416 } else if (strcmp(progname, "mailrm") == 0) {
417 mta_mode = MODE_MCMD;
418 M_cmd = "rm";
419 } else if (strcmp(progname, "runq") == 0) {
420 mta_mode = MODE_RUNQUEUE;
421 do_runq = TRUE;
422 } else if (strcmp(progname, "rmail") == 0) {
423 /* the `rmail' alias should probably be removed now
424 that we have the rmail script. But let's keep it
425 for some while for compatibility. 2010-06-19 */
426 mta_mode = MODE_ACCEPT;
427 opt_i = TRUE;
428 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
429 mta_mode = MODE_SMTP;
430 }
432 /* parse cmd line */
433 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
434 opt = argv[arg] + 1; /* points to the char after the dash */
436 if (strcmp(opt, "-") == 0) {
437 /* everything after `--' are address arguments */
438 arg++;
439 break;
441 } else if (strcmp(opt, "bd") == 0) {
442 do_listen = TRUE;
443 mta_mode = MODE_DAEMON;
445 } else if (strcmp(opt, "bi") == 0) {
446 /* ignored */
447 mta_mode = MODE_BI;
449 } else if (strcmp(opt, "bs") == 0) {
450 mta_mode = MODE_SMTP;
452 } else if (strcmp(opt, "bp") == 0) {
453 mta_mode = MODE_LIST;
455 } else if (strcmp(opt, "bV") == 0) {
456 mta_mode = MODE_VERSION;
458 } else if (strncmp(opt, "B", 1) == 0) {
459 /* we ignore this and throw the argument away */
460 get_optarg(argv, &arg, opt+1);
462 } else if (strncmp(opt, "C", 1) == 0) {
463 conf_file = get_optarg(argv, &arg, opt+1);
464 if (!conf_file) {
465 fprintf(stderr, "-C requires a filename as argument.\n");
466 exit(1);
467 }
469 } else if (strncmp(opt, "d", 1) == 0) {
470 if (getuid() != 0) {
471 fprintf(stderr, "only root may set the debug level.\n");
472 exit(1);
473 }
474 char *lvl = get_optarg(argv, &arg, opt+1);
475 if (!lvl) {
476 fprintf(stderr, "-d requires a number argument.\n");
477 exit(1);
478 }
479 debug_level = atoi(lvl);
481 } else if (strncmp(opt, "f", 1) == 0) {
482 /* set return path */
483 gchar *address = get_optarg(argv, &arg, opt+1);
484 if (!address) {
485 fprintf(stderr, "-f requires an address argument\n");
486 exit(1);
487 }
488 f_address = g_strdup(address);
490 } else if (strncmp(opt, "F", 1) == 0) {
491 full_sender_name = get_optarg(argv, &arg, opt+1);
492 if (!full_sender_name) {
493 fprintf(stderr, "-F requires a name argument\n");
494 exit(1);
495 }
497 } else if (strcmp(opt, "i") == 0) {
498 opt_i = TRUE;
500 } else if (strcmp(opt, "m") == 0) {
501 /* ignore -m (me too) switch (see man page) */
503 } else if (strcmp(opt, "Mrm") == 0) {
504 mta_mode = MODE_MCMD;
505 M_cmd = "rm";
507 } else if (strcmp(opt, "odq") == 0) {
508 do_queue = TRUE;
510 } else if (strcmp(opt, "oi") == 0) {
511 opt_i = TRUE;
513 } else if (strncmp(opt, "o", 1) == 0) {
514 /* ignore all other -oXXX options */
516 } else if (strncmp(opt, "qo", 2) == 0) {
517 mta_mode = MODE_RUNQUEUE;
518 do_runq = FALSE;
519 do_runq_online = TRUE;
520 /* can be NULL, then we use online detection method */
521 route_name = get_optarg(argv, &arg, opt+2);
523 } else if (strncmp(opt, "q", 1) == 0) {
524 /* must be after the `qo' check */
525 gchar *optarg;
527 do_runq = TRUE;
528 mta_mode = MODE_RUNQUEUE;
529 optarg = get_optarg(argv, &arg, opt+1);
530 if (optarg) {
531 /* not just one single queue run but regular runs */
532 mta_mode = MODE_DAEMON;
533 queue_interval = time_interval(optarg);
534 }
536 } else if (strcmp(opt, "t") == 0) {
537 opt_t = TRUE;
539 } else if (strcmp(opt, "v") == 0) {
540 do_verbose = TRUE;
542 } else {
543 fprintf(stderr, "unrecognized option `-%s'\n", opt);
544 exit(1);
545 }
546 }
548 if (!mta_mode) {
549 mta_mode = (arg<argc || opt_t) ? MODE_ACCEPT : MODE_VERSION;
550 }
552 if (mta_mode == MODE_VERSION) {
553 mode_version();
554 exit(0);
555 }
557 /* initialize random generator */
558 srand(time(NULL));
559 /* ignore SIGPIPE signal */
560 signal(SIGPIPE, SIG_IGN);
562 /* close all possibly open file descriptors, except std{in,out,err} */
563 {
564 int i, max_fd = sysconf(_SC_OPEN_MAX);
566 if (max_fd <= 0) {
567 max_fd = 64;
568 }
569 for (i=3; i<max_fd; i++) {
570 close(i);
571 }
572 }
574 init_conf();
576 /* if we are not privileged, and the config file was changed we
577 implicetely set the the run_as_user flag and give up all
578 privileges.
580 So it is possible for a user to run his own daemon without
581 breaking security.
582 */
583 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
584 conf.run_as_user = TRUE;
585 seteuid(conf.orig_uid);
586 setegid(conf.orig_gid);
587 setuid(conf.orig_uid);
588 setgid(conf.orig_gid);
589 }
591 conf.log_dir = LOG_DIR;
592 logopen();
593 if (!read_conf(conf_file)) {
594 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
595 exit(5);
596 }
597 logclose();
599 if (do_queue) {
600 conf.do_queue = TRUE;
601 }
602 if (do_verbose) {
603 conf.do_verbose = TRUE;
604 }
605 if (debug_level >= 0) { /* if >= 0, it was given by argument */
606 conf.debug_level = debug_level;
607 }
609 /* It appears that changing to / ensures that we are never in
610 a directory which we cannot access. This situation could be
611 possible after changing identity.
612 Maybe we should only change to / if we not run as user, to
613 allow relative paths for log files in test setups for
614 instance.
615 */
616 chdir("/");
618 if (!conf.run_as_user) {
619 if (setgid(0) != 0) {
620 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
621 exit(1);
622 }
623 if (setuid(0) != 0) {
624 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
625 exit(1);
626 }
627 }
629 if (!logopen()) {
630 fprintf(stderr, "could not open log file\n");
631 exit(1);
632 }
634 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
636 DEBUG(5) {
637 gchar **str = argv;
638 debugf("args: \n");
639 while (*str) {
640 debugf("%s \n", *str);
641 str++;
642 }
643 }
644 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
646 if (f_address) {
647 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
648 g_free(f_address);
649 if (!return_path) {
650 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
651 exit(1);
652 }
653 }
655 switch (mta_mode) {
656 case MODE_DAEMON:
657 mode_daemon(do_listen, queue_interval, argv);
658 break;
660 case MODE_RUNQUEUE:
661 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
662 break;
664 case MODE_SMTP:
665 mode_smtp();
666 break;
668 case MODE_LIST:
669 queue_list();
670 break;
672 case MODE_BI:
673 exit(0);
674 break; /* well... */
676 case MODE_MCMD:
677 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
678 break;
680 case MODE_ACCEPT:
681 {
682 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
683 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
684 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
685 exit(0);
686 }
687 break;
689 default:
690 fprintf(stderr, "unknown mode: %d\n", mta_mode);
691 break;
692 }
694 logclose();
696 exit(0);
697 }