masqmail

view src/masqmail.c @ 259:f4117fd5a163

made default mode sensible Until now in case no mode was specified, a message would have been read from stdin but then it would fail because no recipients had been given on the cmd line. (This had been useful only if -t was given.) Now in case of no mode being specified neither address arguments or -t is given then the version message is printed, otherwise mode accept is entered (as until yet).
author markus schnalke <meillo@marmaro.de>
date Thu, 02 Dec 2010 16:22:55 -0300
parents 05fa719b7002
children bd3109ec0f0a
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 gchar*
118 get_progname(gchar * arg0)
119 {
120 gchar *p = arg0 + strlen(arg0) - 1;
121 while (p > arg0) {
122 if (*p == '/')
123 return p + 1;
124 p--;
125 }
126 return p;
127 }
129 gboolean
130 write_pidfile(gchar * name)
131 {
132 FILE *fptr;
134 if ((fptr = fopen(name, "wt"))) {
135 fprintf(fptr, "%d\n", getpid());
136 fclose(fptr);
137 pidfile = strdup(name);
138 return TRUE;
139 }
140 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
141 return FALSE;
142 }
144 static void
145 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
146 {
147 guint pid;
149 /* daemon */
150 if (!conf.run_as_user) {
151 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
152 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
153 exit(EXIT_FAILURE);
154 }
155 }
157 /* reparent to init only if init is not already the parent */
158 if (getppid() != 1) {
159 if ((pid = fork()) > 0) {
160 exit(EXIT_SUCCESS);
161 } else if (pid < 0) {
162 logwrite(LOG_ALERT, "could not fork!\n");
163 exit(EXIT_FAILURE);
164 }
165 }
167 signal(SIGTERM, sigterm_handler);
168 write_pidfile(PIDFILEDIR "/masqmail.pid");
170 conf.do_verbose = FALSE;
172 /* closing and reopening the log ensures that it is open afterwards
173 because it is possible that the log is assigned to fd 1 and gets
174 thus closes by fclose(stdout). Similar for the debugfile.
175 */
176 logclose();
177 fclose(stdin);
178 fclose(stdout);
179 fclose(stderr);
180 logopen();
182 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
183 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
184 }
186 static void
187 mode_smtp()
188 {
189 /* accept smtp message on stdin */
190 /* write responses to stderr. */
192 struct sockaddr_in saddr;
193 gchar *peername = NULL;
194 int dummy = sizeof(saddr);
196 conf.do_verbose = FALSE;
198 if (!conf.run_as_user) {
199 seteuid(conf.orig_uid);
200 setegid(conf.orig_gid);
201 }
203 DEBUG(5) debugf("accepting smtp message on stdin\n");
205 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
206 peername = g_strdup(inet_ntoa(saddr.sin_addr));
207 } else if (errno != ENOTSOCK)
208 exit(EXIT_FAILURE);
210 smtp_in(stdin, stderr, peername, NULL);
211 }
213 static void
214 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
215 {
216 /* accept message on stdin */
217 accept_error err;
218 message *msg = create_message();
219 gint i;
221 if (return_path && !is_privileged_user(conf.orig_uid)) {
222 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
223 exit(EXIT_FAILURE);
224 }
226 if (!conf.run_as_user) {
227 seteuid(conf.orig_uid);
228 setegid(conf.orig_gid);
229 }
231 DEBUG(5) debugf("accepting message on stdin\n");
233 msg->received_prot = PROT_LOCAL;
234 for (i = 0; i < addr_cnt; i++) {
235 if (addresses[i][0] != '|')
236 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
237 else {
238 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
239 exit(EXIT_FAILURE);
240 }
241 }
243 /* -f option */
244 msg->return_path = return_path;
246 /* -F option */
247 msg->full_sender_name = full_sender_name;
249 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
250 if (spool_write(msg, TRUE)) {
251 pid_t pid;
252 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
254 if (!conf.do_queue) {
255 if ((pid = fork()) == 0) {
256 conf.do_verbose = FALSE;
257 fclose(stdin);
258 fclose(stdout);
259 fclose(stderr);
260 if (deliver(msg)) {
261 exit(EXIT_SUCCESS);
262 } else
263 exit(EXIT_FAILURE);
264 } else if (pid < 0) {
265 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
266 }
267 }
268 } else {
269 fprintf(stderr, "Could not write spool file\n");
270 exit(EXIT_FAILURE);
271 }
272 } else {
273 switch (err) {
274 case AERR_EOF:
275 fprintf(stderr, "unexpected EOF.\n");
276 exit(EXIT_FAILURE);
277 case AERR_NORCPT:
278 fprintf(stderr, "no recipients.\n");
279 exit(EXIT_FAILURE);
280 case AERR_SIZE:
281 fprintf(stderr, "max message size exceeded.\n");
282 exit(EXIT_FAILURE);
283 default:
284 /* should never happen: */
285 fprintf(stderr, "Unknown error (%d)\r\n", err);
286 exit(EXIT_FAILURE);
287 }
288 exit(EXIT_FAILURE);
289 }
290 }
292 /*
293 currently only the `rm' command is supported
294 until this changes, we don't need any facility for further commands
295 return success if at least one message had been deleted
296 */
297 static int
298 manipulate_queue(char* cmd, char* id[])
299 {
300 gboolean ok = FALSE;
302 if (strcmp(cmd, "rm") != 0) {
303 fprintf(stderr, "unknown command %s\n", cmd);
304 return FALSE;
305 }
307 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
309 /* privileged users may delete any mail */
310 if (is_privileged_user(conf.orig_uid)) {
311 for (; *id; id++) {
312 fprintf(stderr, "id: %s\n", *id);
313 if (queue_delete(*id)) {
314 ok = TRUE;
315 }
316 }
317 return ok;
318 }
320 struct passwd *pw = getpwuid(conf.orig_uid);
321 if (!pw) {
322 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
323 conf.orig_uid, strerror(errno));
324 return FALSE;
325 }
327 /* non-privileged users may only delete their own messages */
328 for (; *id; id++) {
329 message *msg = msg_spool_read(*id, FALSE);
331 fprintf(stderr, "id: %s\n", *id);
333 if (!msg->ident) {
334 fprintf(stderr, "message %s does not have an ident\n", *id);
335 continue;
336 }
337 if (strcmp(pw->pw_name, msg->ident) != 0) {
338 fprintf(stderr, "you do not own message id %s\n", *id);
339 continue;
340 }
342 if ( (msg->received_host || (msg->received_prot != PROT_LOCAL))
343 #ifdef ENABLE_IDENT
344 && !is_in_netlist(msg->received_host, conf.ident_trusted_nets)
345 #endif
346 ) {
347 fprintf(stderr, "message %s was not received locally or from a trusted network\n", *id);
348 continue;
349 }
351 ok = queue_delete(*id);
352 }
353 return ok;
354 }
356 static int
357 run_queue(gboolean do_runq, gboolean do_runq_online, char* route_name)
358 {
359 int ret;
361 /* queue runs */
362 set_identity(conf.orig_uid, "queue run");
364 if (do_runq) {
365 ret = queue_run();
366 }
368 if (do_runq_online) {
369 if (route_name) {
370 conf.online_detect = g_strdup("argument");
371 set_online_name(route_name);
372 }
373 ret = queue_run_online();
374 }
375 return ret;
376 }
378 int
379 main(int argc, char *argv[])
380 {
381 gchar *progname;
382 char* opt;
383 gint arg;
385 mta_mode mta_mode = MODE_NONE;
386 gboolean do_listen = FALSE;
387 gboolean do_runq = FALSE;
388 gboolean do_runq_online = FALSE;
389 gboolean do_queue = FALSE;
390 gint queue_interval = 0;
391 gchar *M_cmd = NULL;
392 gboolean opt_t = FALSE;
393 gboolean opt_i = FALSE;
394 gint exit_code = EXIT_SUCCESS;
395 gchar *conf_file = CONF_FILE;
396 gchar *route_name = NULL;
397 gchar *f_address = NULL;
398 address *return_path = NULL; /* may be changed by -f option */
399 gchar *full_sender_name = NULL;
400 gboolean do_verbose = FALSE;
401 gint debug_level = -1;
403 progname = get_progname(argv[0]);
405 if (strcmp(progname, "mailq") == 0) {
406 mta_mode = MODE_LIST;
407 } else if (strcmp(progname, "mailrm") == 0) {
408 mta_mode = MODE_MCMD;
409 M_cmd = "rm";
410 } else if (strcmp(progname, "runq") == 0) {
411 mta_mode = MODE_RUNQUEUE;
412 do_runq = TRUE;
413 } else if (strcmp(progname, "rmail") == 0) {
414 /* the `rmail' alias should probably be removed now
415 that we have the rmail script. But let's keep it
416 for some while for compatibility. 2010-06-19 */
417 mta_mode = MODE_ACCEPT;
418 opt_i = TRUE;
419 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
420 mta_mode = MODE_SMTP;
421 }
423 /* parse cmd line */
424 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
425 opt = argv[arg] + 1; /* points to the char after the dash */
427 if (strcmp(opt, "-") == 0) {
428 /* everything after `--' are address arguments */
429 arg++;
430 break;
432 } else if (strcmp(opt, "bd") == 0) {
433 do_listen = TRUE;
434 mta_mode = MODE_DAEMON;
436 } else if (strcmp(opt, "bi") == 0) {
437 /* ignored */
438 mta_mode = MODE_BI;
440 } else if (strcmp(opt, "bs") == 0) {
441 mta_mode = MODE_SMTP;
443 } else if (strcmp(opt, "bp") == 0) {
444 mta_mode = MODE_LIST;
446 } else if (strcmp(opt, "bV") == 0) {
447 mta_mode = MODE_VERSION;
449 } else if (strncmp(opt, "B", 1) == 0) {
450 /* we ignore this and throw the argument away */
451 get_optarg(argv, &arg, opt+1);
453 } else if (strncmp(opt, "C", 1) == 0) {
454 conf_file = get_optarg(argv, &arg, opt+1);
455 if (!conf_file) {
456 fprintf(stderr, "-C requires a filename as argument.\n");
457 exit(EXIT_FAILURE);
458 }
460 } else if (strncmp(opt, "d", 1) == 0) {
461 if (getuid() != 0) {
462 fprintf(stderr, "only root may set the debug level.\n");
463 exit(EXIT_FAILURE);
464 }
465 char *lvl = get_optarg(argv, &arg, opt+1);
466 if (!lvl) {
467 fprintf(stderr, "-d requires a number argument.\n");
468 exit(EXIT_FAILURE);
469 }
470 debug_level = atoi(lvl);
472 } else if (strncmp(opt, "f", 1) == 0) {
473 /* set return path */
474 gchar *address = get_optarg(argv, &arg, opt+1);
475 if (!address) {
476 fprintf(stderr, "-f requires an address argument\n");
477 exit(EXIT_FAILURE);
478 }
479 f_address = g_strdup(address);
481 } else if (strncmp(opt, "F", 1) == 0) {
482 full_sender_name = get_optarg(argv, &arg, opt+1);
483 if (!full_sender_name) {
484 fprintf(stderr, "-F requires a name argument\n");
485 exit(EXIT_FAILURE);
486 }
488 } else if (strcmp(opt, "i") == 0) {
489 opt_i = TRUE;
491 } else if (strcmp(opt, "m") == 0) {
492 /* ignore -m (me too) switch (see man page) */
494 } else if (strcmp(opt, "Mrm") == 0) {
495 mta_mode = MODE_MCMD;
496 M_cmd = "rm";
498 } else if (strcmp(opt, "odq") == 0) {
499 do_queue = TRUE;
501 } else if (strcmp(opt, "oi") == 0) {
502 opt_i = TRUE;
504 } else if (strncmp(opt, "o", 1) == 0) {
505 /* ignore all other -oXXX options */
507 } else if (strncmp(opt, "qo", 2) == 0) {
508 mta_mode = MODE_RUNQUEUE;
509 do_runq = FALSE;
510 do_runq_online = TRUE;
511 /* can be NULL, then we use online detection method */
512 route_name = get_optarg(argv, &arg, opt+2);
514 } else if (strncmp(opt, "q", 1) == 0) {
515 /* must be after the `qo' check */
516 gchar *optarg;
518 do_runq = TRUE;
519 mta_mode = MODE_RUNQUEUE;
520 optarg = get_optarg(argv, &arg, opt+1);
521 if (optarg) {
522 /* not just one single queue run but regular runs */
523 mta_mode = MODE_DAEMON;
524 queue_interval = time_interval(optarg);
525 }
527 } else if (strcmp(opt, "t") == 0) {
528 opt_t = TRUE;
530 } else if (strcmp(opt, "v") == 0) {
531 do_verbose = TRUE;
533 } else {
534 fprintf(stderr, "unrecognized option `-%s'\n", opt);
535 exit(EXIT_FAILURE);
536 }
537 }
539 if (!mta_mode) {
540 fprintf(stderr, "arg:%d argc:%d\n", arg, argc);
541 mta_mode = (arg<argc || opt_t) ? MODE_ACCEPT : MODE_VERSION;
542 }
544 if (mta_mode == MODE_VERSION) {
545 gchar *with_resolver = "";
546 gchar *with_auth = "";
547 gchar *with_ident = "";
549 #ifdef ENABLE_RESOLVER
550 with_resolver = " +resolver";
551 #endif
552 #ifdef ENABLE_AUTH
553 with_auth = " +auth";
554 #endif
555 #ifdef ENABLE_IDENT
556 with_ident = " +ident";
557 #endif
559 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
561 exit(EXIT_SUCCESS);
562 }
564 /* initialize random generator */
565 srand(time(NULL));
566 /* ignore SIGPIPE signal */
567 signal(SIGPIPE, SIG_IGN);
569 /* close all possibly open file descriptors, except std{in,out,err} */
570 {
571 int i, max_fd = sysconf(_SC_OPEN_MAX);
573 if (max_fd <= 0) {
574 max_fd = 64;
575 }
576 for (i=3; i<max_fd; i++) {
577 close(i);
578 }
579 }
581 init_conf();
583 /* if we are not privileged, and the config file was changed we
584 implicetely set the the run_as_user flag and give up all
585 privileges.
587 So it is possible for a user to run his own daemon without
588 breaking security.
589 */
590 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
591 conf.run_as_user = TRUE;
592 seteuid(conf.orig_uid);
593 setegid(conf.orig_gid);
594 setuid(conf.orig_uid);
595 setgid(conf.orig_gid);
596 }
598 conf.log_dir = LOG_DIR;
599 logopen();
600 if (!read_conf(conf_file)) {
601 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
602 exit(5);
603 }
604 logclose();
606 if (do_queue) {
607 conf.do_queue = TRUE;
608 }
609 if (do_verbose) {
610 conf.do_verbose = TRUE;
611 }
612 if (debug_level >= 0) { /* if >= 0, it was given by argument */
613 conf.debug_level = debug_level;
614 }
616 /* It appears that changing to / ensures that we are never in
617 a directory which we cannot access. This situation could be
618 possible after changing identity.
619 Maybe we should only change to / if we not run as user, to
620 allow relative paths for log files in test setups for
621 instance.
622 */
623 chdir("/");
625 if (!conf.run_as_user) {
626 if (setgid(0) != 0) {
627 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
628 exit(EXIT_FAILURE);
629 }
630 if (setuid(0) != 0) {
631 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
632 exit(EXIT_FAILURE);
633 }
634 }
636 if (!logopen()) {
637 fprintf(stderr, "could not open log file\n");
638 exit(EXIT_FAILURE);
639 }
641 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
643 DEBUG(5) {
644 gchar **str = argv;
645 debugf("args: \n");
646 while (*str) {
647 debugf("%s \n", *str);
648 str++;
649 }
650 }
651 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
653 if (f_address) {
654 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
655 g_free(f_address);
656 if (!return_path) {
657 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
658 exit(EXIT_FAILURE);
659 }
660 }
662 switch (mta_mode) {
663 case MODE_DAEMON:
664 mode_daemon(do_listen, queue_interval, argv);
665 break;
667 case MODE_RUNQUEUE:
668 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
669 break;
671 case MODE_SMTP:
672 mode_smtp();
673 break;
675 case MODE_LIST:
676 queue_list();
677 break;
679 case MODE_BI:
680 exit(EXIT_SUCCESS);
681 break; /* well... */
683 case MODE_MCMD:
684 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
685 break;
687 case MODE_ACCEPT:
688 {
689 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
690 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
691 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
692 exit(0);
693 }
694 break;
696 default:
697 fprintf(stderr, "unknown mode: %d\n", mta_mode);
698 break;
699 }
701 logclose();
703 exit(exit_code);
704 }