masqmail
view src/masqmail.c @ 260:bd3109ec0f0a
substituted get_progname() with a call to strrchr()
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Thu, 02 Dec 2010 16:45:26 -0300 |
parents | f4117fd5a163 |
children | 0afe18a9ee03 |
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(EXIT_FAILURE);
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(EXIT_SUCCESS);
149 } else if (pid < 0) {
150 logwrite(LOG_ALERT, "could not fork!\n");
151 exit(EXIT_FAILURE);
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(EXIT_FAILURE);
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(EXIT_FAILURE);
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(EXIT_FAILURE);
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(EXIT_SUCCESS);
250 } else
251 exit(EXIT_FAILURE);
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(EXIT_FAILURE);
259 }
260 } else {
261 switch (err) {
262 case AERR_EOF:
263 fprintf(stderr, "unexpected EOF.\n");
264 exit(EXIT_FAILURE);
265 case AERR_NORCPT:
266 fprintf(stderr, "no recipients.\n");
267 exit(EXIT_FAILURE);
268 case AERR_SIZE:
269 fprintf(stderr, "max message size exceeded.\n");
270 exit(EXIT_FAILURE);
271 default:
272 /* should never happen: */
273 fprintf(stderr, "Unknown error (%d)\r\n", err);
274 exit(EXIT_FAILURE);
275 }
276 exit(EXIT_FAILURE);
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 int
367 main(int argc, char *argv[])
368 {
369 gchar *progname;
370 char* opt;
371 gint arg;
373 mta_mode mta_mode = MODE_NONE;
374 gboolean do_listen = FALSE;
375 gboolean do_runq = FALSE;
376 gboolean do_runq_online = FALSE;
377 gboolean do_queue = FALSE;
378 gint queue_interval = 0;
379 gchar *M_cmd = NULL;
380 gboolean opt_t = FALSE;
381 gboolean opt_i = FALSE;
382 gint exit_code = EXIT_SUCCESS;
383 gchar *conf_file = CONF_FILE;
384 gchar *route_name = NULL;
385 gchar *f_address = NULL;
386 address *return_path = NULL; /* may be changed by -f option */
387 gchar *full_sender_name = NULL;
388 gboolean do_verbose = FALSE;
389 gint debug_level = -1;
391 /* strip the path part */
392 progname = strrchr(argc[0], '/');
393 progname = (progname) ? progname+1 : argc[0];
395 if (strcmp(progname, "mailq") == 0) {
396 mta_mode = MODE_LIST;
397 } else if (strcmp(progname, "mailrm") == 0) {
398 mta_mode = MODE_MCMD;
399 M_cmd = "rm";
400 } else if (strcmp(progname, "runq") == 0) {
401 mta_mode = MODE_RUNQUEUE;
402 do_runq = TRUE;
403 } else if (strcmp(progname, "rmail") == 0) {
404 /* the `rmail' alias should probably be removed now
405 that we have the rmail script. But let's keep it
406 for some while for compatibility. 2010-06-19 */
407 mta_mode = MODE_ACCEPT;
408 opt_i = TRUE;
409 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
410 mta_mode = MODE_SMTP;
411 }
413 /* parse cmd line */
414 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
415 opt = argv[arg] + 1; /* points to the char after the dash */
417 if (strcmp(opt, "-") == 0) {
418 /* everything after `--' are address arguments */
419 arg++;
420 break;
422 } else if (strcmp(opt, "bd") == 0) {
423 do_listen = TRUE;
424 mta_mode = MODE_DAEMON;
426 } else if (strcmp(opt, "bi") == 0) {
427 /* ignored */
428 mta_mode = MODE_BI;
430 } else if (strcmp(opt, "bs") == 0) {
431 mta_mode = MODE_SMTP;
433 } else if (strcmp(opt, "bp") == 0) {
434 mta_mode = MODE_LIST;
436 } else if (strcmp(opt, "bV") == 0) {
437 mta_mode = MODE_VERSION;
439 } else if (strncmp(opt, "B", 1) == 0) {
440 /* we ignore this and throw the argument away */
441 get_optarg(argv, &arg, opt+1);
443 } else if (strncmp(opt, "C", 1) == 0) {
444 conf_file = get_optarg(argv, &arg, opt+1);
445 if (!conf_file) {
446 fprintf(stderr, "-C requires a filename as argument.\n");
447 exit(EXIT_FAILURE);
448 }
450 } else if (strncmp(opt, "d", 1) == 0) {
451 if (getuid() != 0) {
452 fprintf(stderr, "only root may set the debug level.\n");
453 exit(EXIT_FAILURE);
454 }
455 char *lvl = get_optarg(argv, &arg, opt+1);
456 if (!lvl) {
457 fprintf(stderr, "-d requires a number argument.\n");
458 exit(EXIT_FAILURE);
459 }
460 debug_level = atoi(lvl);
462 } else if (strncmp(opt, "f", 1) == 0) {
463 /* set return path */
464 gchar *address = get_optarg(argv, &arg, opt+1);
465 if (!address) {
466 fprintf(stderr, "-f requires an address argument\n");
467 exit(EXIT_FAILURE);
468 }
469 f_address = g_strdup(address);
471 } else if (strncmp(opt, "F", 1) == 0) {
472 full_sender_name = get_optarg(argv, &arg, opt+1);
473 if (!full_sender_name) {
474 fprintf(stderr, "-F requires a name argument\n");
475 exit(EXIT_FAILURE);
476 }
478 } else if (strcmp(opt, "i") == 0) {
479 opt_i = TRUE;
481 } else if (strcmp(opt, "m") == 0) {
482 /* ignore -m (me too) switch (see man page) */
484 } else if (strcmp(opt, "Mrm") == 0) {
485 mta_mode = MODE_MCMD;
486 M_cmd = "rm";
488 } else if (strcmp(opt, "odq") == 0) {
489 do_queue = TRUE;
491 } else if (strcmp(opt, "oi") == 0) {
492 opt_i = TRUE;
494 } else if (strncmp(opt, "o", 1) == 0) {
495 /* ignore all other -oXXX options */
497 } else if (strncmp(opt, "qo", 2) == 0) {
498 mta_mode = MODE_RUNQUEUE;
499 do_runq = FALSE;
500 do_runq_online = TRUE;
501 /* can be NULL, then we use online detection method */
502 route_name = get_optarg(argv, &arg, opt+2);
504 } else if (strncmp(opt, "q", 1) == 0) {
505 /* must be after the `qo' check */
506 gchar *optarg;
508 do_runq = TRUE;
509 mta_mode = MODE_RUNQUEUE;
510 optarg = get_optarg(argv, &arg, opt+1);
511 if (optarg) {
512 /* not just one single queue run but regular runs */
513 mta_mode = MODE_DAEMON;
514 queue_interval = time_interval(optarg);
515 }
517 } else if (strcmp(opt, "t") == 0) {
518 opt_t = TRUE;
520 } else if (strcmp(opt, "v") == 0) {
521 do_verbose = TRUE;
523 } else {
524 fprintf(stderr, "unrecognized option `-%s'\n", opt);
525 exit(EXIT_FAILURE);
526 }
527 }
529 if (!mta_mode) {
530 mta_mode = (arg<argc || opt_t) ? MODE_ACCEPT : MODE_VERSION;
531 }
533 if (mta_mode == MODE_VERSION) {
534 gchar *with_resolver = "";
535 gchar *with_auth = "";
536 gchar *with_ident = "";
538 #ifdef ENABLE_RESOLVER
539 with_resolver = " +resolver";
540 #endif
541 #ifdef ENABLE_AUTH
542 with_auth = " +auth";
543 #endif
544 #ifdef ENABLE_IDENT
545 with_ident = " +ident";
546 #endif
548 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
550 exit(EXIT_SUCCESS);
551 }
553 /* initialize random generator */
554 srand(time(NULL));
555 /* ignore SIGPIPE signal */
556 signal(SIGPIPE, SIG_IGN);
558 /* close all possibly open file descriptors, except std{in,out,err} */
559 {
560 int i, max_fd = sysconf(_SC_OPEN_MAX);
562 if (max_fd <= 0) {
563 max_fd = 64;
564 }
565 for (i=3; i<max_fd; i++) {
566 close(i);
567 }
568 }
570 init_conf();
572 /* if we are not privileged, and the config file was changed we
573 implicetely set the the run_as_user flag and give up all
574 privileges.
576 So it is possible for a user to run his own daemon without
577 breaking security.
578 */
579 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
580 conf.run_as_user = TRUE;
581 seteuid(conf.orig_uid);
582 setegid(conf.orig_gid);
583 setuid(conf.orig_uid);
584 setgid(conf.orig_gid);
585 }
587 conf.log_dir = LOG_DIR;
588 logopen();
589 if (!read_conf(conf_file)) {
590 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
591 exit(5);
592 }
593 logclose();
595 if (do_queue) {
596 conf.do_queue = TRUE;
597 }
598 if (do_verbose) {
599 conf.do_verbose = TRUE;
600 }
601 if (debug_level >= 0) { /* if >= 0, it was given by argument */
602 conf.debug_level = debug_level;
603 }
605 /* It appears that changing to / ensures that we are never in
606 a directory which we cannot access. This situation could be
607 possible after changing identity.
608 Maybe we should only change to / if we not run as user, to
609 allow relative paths for log files in test setups for
610 instance.
611 */
612 chdir("/");
614 if (!conf.run_as_user) {
615 if (setgid(0) != 0) {
616 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
617 exit(EXIT_FAILURE);
618 }
619 if (setuid(0) != 0) {
620 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
621 exit(EXIT_FAILURE);
622 }
623 }
625 if (!logopen()) {
626 fprintf(stderr, "could not open log file\n");
627 exit(EXIT_FAILURE);
628 }
630 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
632 DEBUG(5) {
633 gchar **str = argv;
634 debugf("args: \n");
635 while (*str) {
636 debugf("%s \n", *str);
637 str++;
638 }
639 }
640 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
642 if (f_address) {
643 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
644 g_free(f_address);
645 if (!return_path) {
646 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
647 exit(EXIT_FAILURE);
648 }
649 }
651 switch (mta_mode) {
652 case MODE_DAEMON:
653 mode_daemon(do_listen, queue_interval, argv);
654 break;
656 case MODE_RUNQUEUE:
657 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
658 break;
660 case MODE_SMTP:
661 mode_smtp();
662 break;
664 case MODE_LIST:
665 queue_list();
666 break;
668 case MODE_BI:
669 exit(EXIT_SUCCESS);
670 break; /* well... */
672 case MODE_MCMD:
673 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
674 break;
676 case MODE_ACCEPT:
677 {
678 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
679 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
680 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
681 exit(0);
682 }
683 break;
685 default:
686 fprintf(stderr, "unknown mode: %d\n", mta_mode);
687 break;
688 }
690 logclose();
692 exit(exit_code);
693 }