masqmail

view src/masqmail.c @ 266:ab39047ffe44

refactored mode_accept()
author markus schnalke <meillo@marmaro.de>
date Fri, 03 Dec 2010 11:30:24 -0300
parents 1e5e457dea18
children 8be687c06c20
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;
211 pid_t pid;
213 if (return_path && !is_privileged_user(conf.orig_uid)) {
214 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
215 exit(1);
216 }
218 if (!conf.run_as_user) {
219 seteuid(conf.orig_uid);
220 setegid(conf.orig_gid);
221 }
223 DEBUG(5) debugf("accepting message on stdin\n");
225 msg->received_prot = PROT_LOCAL;
226 for (i = 0; i < addr_cnt; i++) {
227 if (addresses[i][0] == '|') {
228 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
229 exit(1);
230 }
231 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
232 }
234 /* -f option */
235 msg->return_path = return_path;
237 /* -F option */
238 msg->full_sender_name = full_sender_name;
240 err = accept_message(stdin, msg, accept_flags);
242 switch (err) {
243 case AERR_OK:
244 /* to continue; all other cases exit */
245 break;
246 case AERR_EOF:
247 fprintf(stderr, "unexpected EOF.\n");
248 exit(1);
249 case AERR_NORCPT:
250 fprintf(stderr, "no recipients.\n");
251 exit(1);
252 case AERR_SIZE:
253 fprintf(stderr, "max message size exceeded.\n");
254 exit(1);
255 default:
256 /* should never happen: */
257 fprintf(stderr, "Unknown error (%d)\r\n", err);
258 exit(1);
259 }
261 if (!spool_write(msg, TRUE)) {
262 fprintf(stderr, "Could not write spool file\n");
263 exit(1);
264 }
266 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
268 if (conf.do_queue) {
269 /* we're finished as we only need to queue it */
270 return;
271 }
273 /* deliver at once */
274 if ((pid = fork()) < 0) {
275 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
276 } else if (pid == 0) {
277 conf.do_verbose = FALSE;
278 fclose(stdin);
279 fclose(stdout);
280 fclose(stderr);
281 if (deliver(msg)) {
282 exit(0);
283 } else {
284 exit(1);
285 }
286 }
287 }
289 /*
290 if -Mrm is given
292 currently only the `rm' command is supported
293 until this changes, we don't need any facility for further commands
294 return success if at least one message had been deleted
295 */
296 static int
297 manipulate_queue(char* cmd, char* id[])
298 {
299 gboolean ok = FALSE;
301 if (strcmp(cmd, "rm") != 0) {
302 fprintf(stderr, "unknown command %s\n", cmd);
303 return FALSE;
304 }
306 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
308 /* privileged users may delete any mail */
309 if (is_privileged_user(conf.orig_uid)) {
310 for (; *id; id++) {
311 fprintf(stderr, "id: %s\n", *id);
312 if (queue_delete(*id)) {
313 ok = TRUE;
314 }
315 }
316 return ok;
317 }
319 struct passwd *pw = getpwuid(conf.orig_uid);
320 if (!pw) {
321 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
322 conf.orig_uid, strerror(errno));
323 return FALSE;
324 }
326 /* non-privileged users may only delete their own messages */
327 for (; *id; id++) {
328 message *msg = msg_spool_read(*id, FALSE);
330 fprintf(stderr, "id: %s\n", *id);
332 if (!msg->ident) {
333 fprintf(stderr, "message %s does not have an ident\n", *id);
334 continue;
335 }
336 if (strcmp(pw->pw_name, msg->ident) != 0) {
337 fprintf(stderr, "you do not own message id %s\n", *id);
338 continue;
339 }
341 if ( (msg->received_host || (msg->received_prot != PROT_LOCAL))
342 #ifdef ENABLE_IDENT
343 && !is_in_netlist(msg->received_host, conf.ident_trusted_nets)
344 #endif
345 ) {
346 fprintf(stderr, "message %s was not received locally or from a trusted network\n", *id);
347 continue;
348 }
350 ok = queue_delete(*id);
351 }
352 return ok;
353 }
355 /* -qo, -q (without argument), or called as runq */
356 /* TODO: are -qo and -q exclusively or not?
357 And how is this related to being a daemon? */
358 static int
359 run_queue(gboolean do_runq, gboolean do_runq_online, char* route_name)
360 {
361 int ret;
363 /* queue runs */
364 set_identity(conf.orig_uid, "queue run");
366 if (do_runq) {
367 ret = queue_run();
368 }
370 if (do_runq_online) {
371 if (route_name) {
372 conf.online_detect = g_strdup("argument");
373 set_online_name(route_name);
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 = "";
386 gchar *with_ident = "";
388 #ifdef ENABLE_RESOLVER
389 with_resolver = " +resolver";
390 #endif
391 #ifdef ENABLE_AUTH
392 with_auth = " +auth";
393 #endif
394 #ifdef ENABLE_IDENT
395 with_ident = " +ident";
396 #endif
398 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
399 }
401 int
402 main(int argc, char *argv[])
403 {
404 gchar *progname;
405 char* opt;
406 gint arg;
408 mta_mode mta_mode = MODE_NONE;
409 gboolean do_listen = FALSE;
410 gboolean do_runq = FALSE;
411 gboolean do_runq_online = FALSE;
412 gboolean do_queue = FALSE;
413 gint queue_interval = 0;
414 gchar *M_cmd = NULL;
415 gboolean opt_t = FALSE;
416 gboolean opt_i = FALSE;
417 gchar *conf_file = CONF_FILE;
418 gchar *route_name = NULL;
419 gchar *f_address = NULL;
420 address *return_path = NULL; /* may be changed by -f option */
421 gchar *full_sender_name = NULL;
422 gboolean do_verbose = FALSE;
423 gint debug_level = -1;
425 /* strip the path part */
426 progname = strrchr(argv[0], '/');
427 progname = (progname) ? progname+1 : argv[0];
429 if (strcmp(progname, "mailq") == 0) {
430 mta_mode = MODE_LIST;
431 } else if (strcmp(progname, "mailrm") == 0) {
432 mta_mode = MODE_MCMD;
433 M_cmd = "rm";
434 } else if (strcmp(progname, "runq") == 0) {
435 mta_mode = MODE_RUNQUEUE;
436 do_runq = TRUE;
437 } else if (strcmp(progname, "rmail") == 0) {
438 /* the `rmail' alias should probably be removed now
439 that we have the rmail script. But let's keep it
440 for some while for compatibility. 2010-06-19 */
441 mta_mode = MODE_ACCEPT;
442 opt_i = TRUE;
443 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
444 mta_mode = MODE_SMTP;
445 }
447 /* parse cmd line */
448 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
449 opt = argv[arg] + 1; /* points to the char after the dash */
451 if (strcmp(opt, "-") == 0) {
452 /* everything after `--' are address arguments */
453 arg++;
454 break;
456 } else if (strcmp(opt, "bd") == 0) {
457 do_listen = TRUE;
458 mta_mode = MODE_DAEMON;
460 } else if (strcmp(opt, "bi") == 0) {
461 /* ignored */
462 mta_mode = MODE_BI;
464 } else if (strcmp(opt, "bs") == 0) {
465 mta_mode = MODE_SMTP;
467 } else if (strcmp(opt, "bp") == 0) {
468 mta_mode = MODE_LIST;
470 } else if (strcmp(opt, "bV") == 0) {
471 mta_mode = MODE_VERSION;
473 } else if (strncmp(opt, "B", 1) == 0) {
474 /* we ignore this and throw the argument away */
475 get_optarg(argv, &arg, opt+1);
477 } else if (strncmp(opt, "C", 1) == 0) {
478 conf_file = get_optarg(argv, &arg, opt+1);
479 if (!conf_file) {
480 fprintf(stderr, "-C requires a filename as argument.\n");
481 exit(1);
482 }
484 } else if (strncmp(opt, "d", 1) == 0) {
485 if (getuid() != 0) {
486 fprintf(stderr, "only root may set the debug level.\n");
487 exit(1);
488 }
489 char *lvl = get_optarg(argv, &arg, opt+1);
490 if (!lvl) {
491 fprintf(stderr, "-d requires a number argument.\n");
492 exit(1);
493 }
494 debug_level = atoi(lvl);
496 } else if (strncmp(opt, "f", 1) == 0) {
497 /* set return path */
498 gchar *address = get_optarg(argv, &arg, opt+1);
499 if (!address) {
500 fprintf(stderr, "-f requires an address argument\n");
501 exit(1);
502 }
503 f_address = g_strdup(address);
505 } else if (strncmp(opt, "F", 1) == 0) {
506 full_sender_name = get_optarg(argv, &arg, opt+1);
507 if (!full_sender_name) {
508 fprintf(stderr, "-F requires a name argument\n");
509 exit(1);
510 }
512 } else if (strcmp(opt, "i") == 0) {
513 opt_i = TRUE;
515 } else if (strcmp(opt, "m") == 0) {
516 /* ignore -m (me too) switch (see man page) */
518 } else if (strcmp(opt, "Mrm") == 0) {
519 mta_mode = MODE_MCMD;
520 M_cmd = "rm";
522 } else if (strcmp(opt, "odq") == 0) {
523 do_queue = TRUE;
525 } else if (strcmp(opt, "oi") == 0) {
526 opt_i = TRUE;
528 } else if (strncmp(opt, "o", 1) == 0) {
529 /* ignore all other -oXXX options */
531 } else if (strncmp(opt, "qo", 2) == 0) {
532 mta_mode = MODE_RUNQUEUE;
533 do_runq = FALSE;
534 do_runq_online = TRUE;
535 /* can be NULL, then we use online detection method */
536 route_name = get_optarg(argv, &arg, opt+2);
538 } else if (strncmp(opt, "q", 1) == 0) {
539 /* must be after the `qo' check */
540 gchar *optarg;
542 do_runq = TRUE;
543 mta_mode = MODE_RUNQUEUE;
544 optarg = get_optarg(argv, &arg, opt+1);
545 if (optarg) {
546 /* not just one single queue run but regular runs */
547 mta_mode = MODE_DAEMON;
548 queue_interval = time_interval(optarg);
549 }
551 } else if (strcmp(opt, "t") == 0) {
552 opt_t = TRUE;
554 } else if (strcmp(opt, "v") == 0) {
555 do_verbose = TRUE;
557 } else {
558 fprintf(stderr, "unrecognized option `-%s'\n", opt);
559 exit(1);
560 }
561 }
563 if (!mta_mode) {
564 mta_mode = (arg<argc || opt_t) ? MODE_ACCEPT : MODE_VERSION;
565 }
567 if (mta_mode == MODE_VERSION) {
568 mode_version();
569 exit(0);
570 }
572 /* initialize random generator */
573 srand(time(NULL));
574 /* ignore SIGPIPE signal */
575 signal(SIGPIPE, SIG_IGN);
577 /* close all possibly open file descriptors, except std{in,out,err} */
578 {
579 int i, max_fd = sysconf(_SC_OPEN_MAX);
581 if (max_fd <= 0) {
582 max_fd = 64;
583 }
584 for (i=3; i<max_fd; i++) {
585 close(i);
586 }
587 }
589 init_conf();
591 /* if we are not privileged, and the config file was changed we
592 implicetely set the the run_as_user flag and give up all
593 privileges.
595 So it is possible for a user to run his own daemon without
596 breaking security.
597 */
598 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
599 conf.run_as_user = TRUE;
600 seteuid(conf.orig_uid);
601 setegid(conf.orig_gid);
602 setuid(conf.orig_uid);
603 setgid(conf.orig_gid);
604 }
606 conf.log_dir = LOG_DIR;
607 logopen();
608 if (!read_conf(conf_file)) {
609 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
610 exit(5);
611 }
612 logclose();
614 if (do_queue) {
615 conf.do_queue = TRUE;
616 }
617 if (do_verbose) {
618 conf.do_verbose = TRUE;
619 }
620 if (debug_level >= 0) { /* if >= 0, it was given by argument */
621 conf.debug_level = debug_level;
622 }
624 /* It appears that changing to / ensures that we are never in
625 a directory which we cannot access. This situation could be
626 possible after changing identity.
627 Maybe we should only change to / if we not run as user, to
628 allow relative paths for log files in test setups for
629 instance.
630 */
631 chdir("/");
633 if (!conf.run_as_user) {
634 if (setgid(0) != 0) {
635 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
636 exit(1);
637 }
638 if (setuid(0) != 0) {
639 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
640 exit(1);
641 }
642 }
644 if (!logopen()) {
645 fprintf(stderr, "could not open log file\n");
646 exit(1);
647 }
649 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
651 DEBUG(5) {
652 gchar **str = argv;
653 debugf("args: \n");
654 while (*str) {
655 debugf("%s \n", *str);
656 str++;
657 }
658 }
659 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
661 if (f_address) {
662 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
663 g_free(f_address);
664 if (!return_path) {
665 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
666 exit(1);
667 }
668 }
670 switch (mta_mode) {
671 case MODE_DAEMON:
672 mode_daemon(do_listen, queue_interval, argv);
673 break;
675 case MODE_RUNQUEUE:
676 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
677 break;
679 case MODE_SMTP:
680 mode_smtp();
681 break;
683 case MODE_LIST:
684 queue_list();
685 break;
687 case MODE_BI:
688 exit(0);
689 break; /* well... */
691 case MODE_MCMD:
692 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
693 break;
695 case MODE_ACCEPT:
696 {
697 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
698 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
699 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
700 exit(0);
701 }
702 break;
704 default:
705 fprintf(stderr, "unknown mode: %d\n", mta_mode);
706 break;
707 }
709 logclose();
711 exit(0);
712 }