masqmail

annotate src/masqmail.c @ 281:ea5f86e0a81c

modes are now enforced exclusive Other MTAs (exim, postfix) are more relaxing, but as combinations of exclusive modes are senseless we behave more obvious if we fail early. This makes understanding the behavior easier too.
author markus schnalke <meillo@marmaro.de>
date Tue, 07 Dec 2010 14:04:56 -0300
parents c35c59a36a2a
children ba53e648906f
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@76 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@0 4
meillo@0 5 This program is free software; you can redistribute it and/or modify
meillo@0 6 it under the terms of the GNU General Public License as published by
meillo@0 7 the Free Software Foundation; either version 2 of the License, or
meillo@0 8 (at your option) any later version.
meillo@0 9
meillo@0 10 This program is distributed in the hope that it will be useful,
meillo@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 13 GNU General Public License for more details.
meillo@0 14
meillo@0 15 You should have received a copy of the GNU General Public License
meillo@0 16 along with this program; if not, write to the Free Software
meillo@0 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 18 */
meillo@0 19
meillo@0 20 #include <stdio.h>
meillo@0 21 #include <errno.h>
meillo@0 22 #include <stdlib.h>
meillo@0 23 #include <string.h>
meillo@0 24 #include <unistd.h>
meillo@0 25 #include <sys/types.h>
meillo@0 26 #include <sys/socket.h>
meillo@0 27 #include <sys/time.h>
meillo@0 28 #include <netinet/in.h>
meillo@0 29 #include <netdb.h>
meillo@0 30 #include <syslog.h>
meillo@0 31 #include <signal.h>
meillo@0 32
meillo@0 33 #include <glib.h>
meillo@0 34
meillo@0 35 #include "masqmail.h"
meillo@0 36
meillo@192 37 /* mutually exclusive modes. Note that there is no 'queue daemon' mode.
meillo@192 38 It, as well as the distinction beween the two (non exclusive) daemon
meillo@192 39 (queue and listen) modes, is handled by flags.*/
meillo@281 40 enum mta_mode {
meillo@281 41 MODE_NONE = 0, /* to check if a mode was set */
meillo@281 42 MODE_ACCEPT, /* accept message on stdin (fallback mode) */
meillo@10 43 MODE_DAEMON, /* run as daemon */
meillo@10 44 MODE_RUNQUEUE, /* single queue run, online or offline */
meillo@10 45 MODE_SMTP, /* accept SMTP on stdin */
meillo@10 46 MODE_LIST, /* list queue */
meillo@10 47 MODE_MCMD, /* do queue manipulation */
meillo@10 48 MODE_VERSION, /* show version */
meillo@10 49 MODE_BI, /* fake ;-) */
meillo@281 50 };
meillo@281 51 enum mta_mode mta_mode = MODE_NONE;
meillo@0 52
meillo@0 53 char *pidfile = NULL;
meillo@0 54 volatile int sigterm_in_progress = 0;
meillo@0 55
meillo@10 56 static void
meillo@10 57 sigterm_handler(int sig)
meillo@0 58 {
meillo@10 59 if (sigterm_in_progress)
meillo@10 60 raise(sig);
meillo@10 61 sigterm_in_progress = 1;
meillo@0 62
meillo@10 63 if (pidfile) {
meillo@10 64 uid_t uid;
meillo@10 65 uid = seteuid(0);
meillo@10 66 if (unlink(pidfile) != 0)
meillo@10 67 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
meillo@10 68 seteuid(uid); /* we exit anyway after this, just to be sure */
meillo@10 69 }
meillo@0 70
meillo@10 71 signal(sig, SIG_DFL);
meillo@10 72 raise(sig);
meillo@0 73 }
meillo@0 74
meillo@10 75 #ifdef ENABLE_IDENT /* so far used for that only */
meillo@10 76 static gboolean
meillo@10 77 is_in_netlist(gchar * host, GList * netlist)
meillo@0 78 {
meillo@10 79 guint hostip = inet_addr(host);
meillo@10 80 struct in_addr addr;
meillo@0 81
meillo@10 82 addr.s_addr = hostip;
meillo@10 83 if (addr.s_addr != INADDR_NONE) {
meillo@10 84 GList *node;
meillo@10 85 foreach(netlist, node) {
meillo@10 86 struct in_addr *net = (struct in_addr *) (node->data);
meillo@10 87 if ((addr.s_addr & net->s_addr) == net->s_addr)
meillo@10 88 return TRUE;
meillo@10 89 }
meillo@10 90 }
meillo@10 91 return FALSE;
meillo@0 92 }
meillo@0 93 #endif
meillo@0 94
meillo@249 95 /*
meillo@249 96 argv: the original argv
meillo@249 97 argp: number of arg (may get modified!)
meillo@249 98 cp: pointing to the char after the option
meillo@249 99 e.g. `-d 6' `-d6'
meillo@249 100 ^ ^
meillo@249 101 */
meillo@10 102 gchar*
meillo@249 103 get_optarg(char* argv[], gint* argp, char* cp)
meillo@0 104 {
meillo@249 105 if (*cp) {
meillo@249 106 /* this kind: -xval */
meillo@249 107 return cp;
meillo@249 108 }
meillo@249 109 cp = argv[*argp+1];
meillo@249 110 if (cp && (*cp != '-')) {
meillo@249 111 /* this kind: -x val */
meillo@249 112 (*argp)++;
meillo@249 113 return cp;
meillo@10 114 }
meillo@10 115 return NULL;
meillo@0 116 }
meillo@0 117
meillo@10 118 gboolean
meillo@10 119 write_pidfile(gchar * name)
meillo@0 120 {
meillo@10 121 FILE *fptr;
meillo@0 122
meillo@10 123 if ((fptr = fopen(name, "wt"))) {
meillo@10 124 fprintf(fptr, "%d\n", getpid());
meillo@10 125 fclose(fptr);
meillo@10 126 pidfile = strdup(name);
meillo@10 127 return TRUE;
meillo@10 128 }
meillo@10 129 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
meillo@10 130 return FALSE;
meillo@10 131 }
meillo@0 132
meillo@264 133 /* on -bd or if -q has an argument */
meillo@10 134 static void
meillo@10 135 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
meillo@10 136 {
meillo@10 137 guint pid;
meillo@0 138
meillo@10 139 /* daemon */
meillo@10 140 if (!conf.run_as_user) {
meillo@10 141 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@10 142 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
meillo@262 143 exit(1);
meillo@10 144 }
meillo@10 145 }
meillo@0 146
meillo@74 147 /* reparent to init only if init is not already the parent */
meillo@74 148 if (getppid() != 1) {
meillo@74 149 if ((pid = fork()) > 0) {
meillo@262 150 exit(0);
meillo@74 151 } else if (pid < 0) {
meillo@208 152 logwrite(LOG_ALERT, "could not fork!\n");
meillo@262 153 exit(1);
meillo@74 154 }
meillo@10 155 }
meillo@0 156
meillo@10 157 signal(SIGTERM, sigterm_handler);
meillo@10 158 write_pidfile(PIDFILEDIR "/masqmail.pid");
meillo@0 159
meillo@10 160 conf.do_verbose = FALSE;
meillo@10 161
meillo@72 162 /* closing and reopening the log ensures that it is open afterwards
meillo@72 163 because it is possible that the log is assigned to fd 1 and gets
meillo@72 164 thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 165 */
meillo@72 166 logclose();
meillo@10 167 fclose(stdin);
meillo@10 168 fclose(stdout);
meillo@10 169 fclose(stderr);
meillo@72 170 logopen();
meillo@10 171
meillo@208 172 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
meillo@10 173 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
meillo@0 174 }
meillo@0 175
meillo@264 176 /* -bs or called as smtpd or in.smtpd */
meillo@10 177 static void
meillo@10 178 mode_smtp()
meillo@0 179 {
meillo@10 180 /* accept smtp message on stdin */
meillo@10 181 /* write responses to stderr. */
meillo@0 182
meillo@10 183 struct sockaddr_in saddr;
meillo@10 184 gchar *peername = NULL;
meillo@10 185 int dummy = sizeof(saddr);
meillo@0 186
meillo@10 187 conf.do_verbose = FALSE;
meillo@0 188
meillo@10 189 if (!conf.run_as_user) {
meillo@10 190 seteuid(conf.orig_uid);
meillo@10 191 setegid(conf.orig_gid);
meillo@10 192 }
meillo@0 193
meillo@10 194 DEBUG(5) debugf("accepting smtp message on stdin\n");
meillo@0 195
meillo@10 196 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
meillo@10 197 peername = g_strdup(inet_ntoa(saddr.sin_addr));
meillo@10 198 } else if (errno != ENOTSOCK)
meillo@262 199 exit(1);
meillo@0 200
meillo@10 201 smtp_in(stdin, stderr, peername, NULL);
meillo@0 202 }
meillo@0 203
meillo@264 204 /* default mode if address args or -t is specified, or called as rmail */
meillo@10 205 static void
meillo@10 206 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
meillo@0 207 {
meillo@10 208 /* accept message on stdin */
meillo@10 209 accept_error err;
meillo@10 210 message *msg = create_message();
meillo@10 211 gint i;
meillo@266 212 pid_t pid;
meillo@0 213
meillo@83 214 if (return_path && !is_privileged_user(conf.orig_uid)) {
meillo@96 215 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
meillo@262 216 exit(1);
meillo@10 217 }
meillo@0 218
meillo@10 219 if (!conf.run_as_user) {
meillo@10 220 seteuid(conf.orig_uid);
meillo@10 221 setegid(conf.orig_gid);
meillo@10 222 }
meillo@0 223
meillo@10 224 DEBUG(5) debugf("accepting message on stdin\n");
meillo@0 225
meillo@10 226 msg->received_prot = PROT_LOCAL;
meillo@276 227
meillo@276 228 /* warn if -t option and cmdline addr args */
meillo@276 229 if (addr_cnt && (accept_flags & ACC_RCPT_FROM_HEAD)) {
meillo@276 230 logwrite(LOG_ALERT, "command line address arguments are now *added* to the mail header\\\n");
meillo@276 231 logwrite(LOG_ALERT, " recipient addresses (instead of substracted) when -t is given.\\\n");
meillo@276 232 logwrite(LOG_ALERT, " this changed with version 0.3.1\n");
meillo@276 233 }
meillo@276 234
meillo@10 235 for (i = 0; i < addr_cnt; i++) {
meillo@266 236 if (addresses[i][0] == '|') {
meillo@10 237 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
meillo@276 238 /* should we better ignore this one addr? */
meillo@262 239 exit(1);
meillo@10 240 }
meillo@264 241 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
meillo@10 242 }
meillo@0 243
meillo@10 244 /* -f option */
meillo@10 245 msg->return_path = return_path;
meillo@0 246
meillo@10 247 /* -F option */
meillo@10 248 msg->full_sender_name = full_sender_name;
meillo@0 249
meillo@266 250 err = accept_message(stdin, msg, accept_flags);
meillo@0 251
meillo@266 252 switch (err) {
meillo@266 253 case AERR_OK:
meillo@266 254 /* to continue; all other cases exit */
meillo@266 255 break;
meillo@266 256 case AERR_EOF:
meillo@266 257 fprintf(stderr, "unexpected EOF.\n");
meillo@266 258 exit(1);
meillo@266 259 case AERR_NORCPT:
meillo@266 260 fprintf(stderr, "no recipients.\n");
meillo@266 261 exit(1);
meillo@266 262 case AERR_SIZE:
meillo@266 263 fprintf(stderr, "max message size exceeded.\n");
meillo@266 264 exit(1);
meillo@266 265 default:
meillo@266 266 /* should never happen: */
meillo@266 267 fprintf(stderr, "Unknown error (%d)\r\n", err);
meillo@266 268 exit(1);
meillo@266 269 }
meillo@266 270
meillo@266 271 if (!spool_write(msg, TRUE)) {
meillo@266 272 fprintf(stderr, "Could not write spool file\n");
meillo@266 273 exit(1);
meillo@266 274 }
meillo@266 275
meillo@267 276 /* here the mail is queued and thus in our responsibility */
meillo@266 277 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
meillo@266 278
meillo@266 279 if (conf.do_queue) {
meillo@266 280 /* we're finished as we only need to queue it */
meillo@266 281 return;
meillo@266 282 }
meillo@266 283
meillo@266 284 /* deliver at once */
meillo@266 285 if ((pid = fork()) < 0) {
meillo@266 286 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
meillo@266 287 } else if (pid == 0) {
meillo@266 288 conf.do_verbose = FALSE;
meillo@266 289 fclose(stdin);
meillo@266 290 fclose(stdout);
meillo@266 291 fclose(stderr);
meillo@266 292 if (deliver(msg)) {
meillo@266 293 exit(0);
meillo@10 294 } else {
meillo@267 295 /*
meillo@267 296 TODO:
meillo@267 297 Should we really fail here? Because the mail is queued
meillo@267 298 already. If we fail the client might submit it again.
meillo@267 299 If at-once-delivery is seen as an additional best-effort
meillo@267 300 service, then we should still exit successful here.
meillo@267 301 */
meillo@262 302 exit(1);
meillo@10 303 }
meillo@0 304 }
meillo@0 305 }
meillo@0 306
meillo@250 307 /*
meillo@264 308 if -Mrm is given
meillo@264 309
meillo@250 310 currently only the `rm' command is supported
meillo@250 311 until this changes, we don't need any facility for further commands
meillo@250 312 return success if at least one message had been deleted
meillo@250 313 */
meillo@250 314 static int
meillo@250 315 manipulate_queue(char* cmd, char* id[])
meillo@250 316 {
meillo@250 317 gboolean ok = FALSE;
meillo@250 318
meillo@250 319 if (strcmp(cmd, "rm") != 0) {
meillo@250 320 fprintf(stderr, "unknown command %s\n", cmd);
meillo@250 321 return FALSE;
meillo@250 322 }
meillo@250 323
meillo@250 324 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
meillo@250 325
meillo@250 326 /* privileged users may delete any mail */
meillo@250 327 if (is_privileged_user(conf.orig_uid)) {
meillo@250 328 for (; *id; id++) {
meillo@250 329 fprintf(stderr, "id: %s\n", *id);
meillo@250 330 if (queue_delete(*id)) {
meillo@250 331 ok = TRUE;
meillo@250 332 }
meillo@250 333 }
meillo@250 334 return ok;
meillo@250 335 }
meillo@250 336
meillo@250 337 struct passwd *pw = getpwuid(conf.orig_uid);
meillo@250 338 if (!pw) {
meillo@250 339 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
meillo@250 340 conf.orig_uid, strerror(errno));
meillo@250 341 return FALSE;
meillo@250 342 }
meillo@250 343
meillo@250 344 /* non-privileged users may only delete their own messages */
meillo@250 345 for (; *id; id++) {
meillo@250 346 message *msg = msg_spool_read(*id, FALSE);
meillo@250 347
meillo@250 348 fprintf(stderr, "id: %s\n", *id);
meillo@250 349
meillo@250 350 if (!msg->ident) {
meillo@250 351 fprintf(stderr, "message %s does not have an ident\n", *id);
meillo@250 352 continue;
meillo@250 353 }
meillo@250 354 if (strcmp(pw->pw_name, msg->ident) != 0) {
meillo@250 355 fprintf(stderr, "you do not own message id %s\n", *id);
meillo@250 356 continue;
meillo@250 357 }
meillo@250 358
meillo@250 359 if ( (msg->received_host || (msg->received_prot != PROT_LOCAL))
meillo@250 360 #ifdef ENABLE_IDENT
meillo@250 361 && !is_in_netlist(msg->received_host, conf.ident_trusted_nets)
meillo@250 362 #endif
meillo@250 363 ) {
meillo@250 364 fprintf(stderr, "message %s was not received locally or from a trusted network\n", *id);
meillo@250 365 continue;
meillo@250 366 }
meillo@250 367
meillo@250 368 ok = queue_delete(*id);
meillo@250 369 }
meillo@250 370 return ok;
meillo@250 371 }
meillo@250 372
meillo@264 373 /* -qo, -q (without argument), or called as runq */
meillo@264 374 /* TODO: are -qo and -q exclusively or not?
meillo@264 375 And how is this related to being a daemon? */
meillo@251 376 static int
meillo@251 377 run_queue(gboolean do_runq, gboolean do_runq_online, char* route_name)
meillo@251 378 {
meillo@251 379 int ret;
meillo@251 380
meillo@251 381 /* queue runs */
meillo@251 382 set_identity(conf.orig_uid, "queue run");
meillo@251 383
meillo@251 384 if (do_runq) {
meillo@251 385 ret = queue_run();
meillo@251 386 }
meillo@251 387
meillo@251 388 if (do_runq_online) {
meillo@251 389 if (route_name) {
meillo@251 390 conf.online_detect = g_strdup("argument");
meillo@251 391 set_online_name(route_name);
meillo@251 392 }
meillo@251 393 ret = queue_run_online();
meillo@251 394 }
meillo@251 395 return ret;
meillo@251 396 }
meillo@251 397
meillo@264 398 /* -bV or default mode if neither addr arg nor -t */
meillo@261 399 static void
meillo@261 400 mode_version(void)
meillo@261 401 {
meillo@261 402 gchar *with_resolver = "";
meillo@261 403 gchar *with_auth = "";
meillo@261 404 gchar *with_ident = "";
meillo@261 405
meillo@261 406 #ifdef ENABLE_RESOLVER
meillo@261 407 with_resolver = " +resolver";
meillo@261 408 #endif
meillo@261 409 #ifdef ENABLE_AUTH
meillo@261 410 with_auth = " +auth";
meillo@261 411 #endif
meillo@261 412 #ifdef ENABLE_IDENT
meillo@261 413 with_ident = " +ident";
meillo@261 414 #endif
meillo@261 415
meillo@261 416 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
meillo@261 417 }
meillo@261 418
meillo@281 419 void
meillo@281 420 set_mode(enum mta_mode mode)
meillo@281 421 {
meillo@281 422 if (mta_mode && mta_mode!=mode) {
meillo@281 423 fprintf(stderr, "operation mode was already specified (%d vs. %d)\n", mta_mode, mode);
meillo@281 424 exit(1);
meillo@281 425 }
meillo@281 426
meillo@281 427 mta_mode = mode;
meillo@281 428 return;
meillo@281 429 }
meillo@281 430
meillo@0 431 int
meillo@0 432 main(int argc, char *argv[])
meillo@0 433 {
meillo@251 434 gchar *progname;
meillo@249 435 char* opt;
meillo@249 436 gint arg;
meillo@0 437
meillo@10 438 gboolean do_listen = FALSE;
meillo@10 439 gboolean do_runq = FALSE;
meillo@10 440 gboolean do_runq_online = FALSE;
meillo@10 441 gboolean do_queue = FALSE;
meillo@10 442 gint queue_interval = 0;
meillo@251 443 gchar *M_cmd = NULL;
meillo@10 444 gboolean opt_t = FALSE;
meillo@10 445 gboolean opt_i = FALSE;
meillo@251 446 gchar *conf_file = CONF_FILE;
meillo@10 447 gchar *route_name = NULL;
meillo@10 448 gchar *f_address = NULL;
meillo@251 449 address *return_path = NULL; /* may be changed by -f option */
meillo@10 450 gchar *full_sender_name = NULL;
meillo@251 451 gboolean do_verbose = FALSE;
meillo@251 452 gint debug_level = -1;
meillo@0 453
meillo@260 454 /* strip the path part */
meillo@261 455 progname = strrchr(argv[0], '/');
meillo@261 456 progname = (progname) ? progname+1 : argv[0];
meillo@0 457
meillo@10 458 if (strcmp(progname, "mailq") == 0) {
meillo@10 459 mta_mode = MODE_LIST;
meillo@10 460 } else if (strcmp(progname, "mailrm") == 0) {
meillo@10 461 mta_mode = MODE_MCMD;
meillo@10 462 M_cmd = "rm";
meillo@10 463 } else if (strcmp(progname, "runq") == 0) {
meillo@10 464 mta_mode = MODE_RUNQUEUE;
meillo@10 465 do_runq = TRUE;
meillo@10 466 } else if (strcmp(progname, "rmail") == 0) {
meillo@89 467 /* the `rmail' alias should probably be removed now
meillo@89 468 that we have the rmail script. But let's keep it
meillo@89 469 for some while for compatibility. 2010-06-19 */
meillo@10 470 mta_mode = MODE_ACCEPT;
meillo@10 471 opt_i = TRUE;
meillo@10 472 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
meillo@10 473 mta_mode = MODE_SMTP;
meillo@10 474 }
meillo@0 475
meillo@10 476 /* parse cmd line */
meillo@249 477 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
meillo@251 478 opt = argv[arg] + 1; /* points to the char after the dash */
meillo@10 479
meillo@249 480 if (strcmp(opt, "-") == 0) {
meillo@249 481 /* everything after `--' are address arguments */
meillo@249 482 arg++;
meillo@249 483 break;
meillo@10 484
meillo@278 485 } else if (strcmp(opt, "bm") == 0) {
meillo@281 486 set_mode(MODE_ACCEPT);
meillo@278 487
meillo@249 488 } else if (strcmp(opt, "bd") == 0) {
meillo@281 489 set_mode(MODE_DAEMON);
meillo@249 490 do_listen = TRUE;
meillo@249 491
meillo@249 492 } else if (strcmp(opt, "bi") == 0) {
meillo@281 493 set_mode(MODE_BI);
meillo@249 494
meillo@249 495 } else if (strcmp(opt, "bs") == 0) {
meillo@281 496 set_mode(MODE_SMTP);
meillo@249 497
meillo@249 498 } else if (strcmp(opt, "bp") == 0) {
meillo@281 499 set_mode(MODE_LIST);
meillo@249 500
meillo@249 501 } else if (strcmp(opt, "bV") == 0) {
meillo@281 502 set_mode(MODE_VERSION);
meillo@249 503
meillo@249 504 } else if (strncmp(opt, "B", 1) == 0) {
meillo@249 505 /* we ignore this and throw the argument away */
meillo@249 506 get_optarg(argv, &arg, opt+1);
meillo@249 507
meillo@249 508 } else if (strncmp(opt, "C", 1) == 0) {
meillo@251 509 conf_file = get_optarg(argv, &arg, opt+1);
meillo@251 510 if (!conf_file) {
meillo@249 511 fprintf(stderr, "-C requires a filename as argument.\n");
meillo@262 512 exit(1);
meillo@10 513 }
meillo@249 514
meillo@249 515 } else if (strncmp(opt, "d", 1) == 0) {
meillo@249 516 if (getuid() != 0) {
meillo@249 517 fprintf(stderr, "only root may set the debug level.\n");
meillo@262 518 exit(1);
meillo@249 519 }
meillo@249 520 char *lvl = get_optarg(argv, &arg, opt+1);
meillo@249 521 if (!lvl) {
meillo@249 522 fprintf(stderr, "-d requires a number argument.\n");
meillo@262 523 exit(1);
meillo@249 524 }
meillo@249 525 debug_level = atoi(lvl);
meillo@249 526
meillo@249 527 } else if (strncmp(opt, "f", 1) == 0) {
meillo@249 528 /* set return path */
meillo@249 529 gchar *address = get_optarg(argv, &arg, opt+1);
meillo@249 530 if (!address) {
meillo@249 531 fprintf(stderr, "-f requires an address argument\n");
meillo@262 532 exit(1);
meillo@249 533 }
meillo@249 534 f_address = g_strdup(address);
meillo@249 535
meillo@249 536 } else if (strncmp(opt, "F", 1) == 0) {
meillo@249 537 full_sender_name = get_optarg(argv, &arg, opt+1);
meillo@249 538 if (!full_sender_name) {
meillo@249 539 fprintf(stderr, "-F requires a name argument\n");
meillo@262 540 exit(1);
meillo@249 541 }
meillo@249 542
meillo@249 543 } else if (strcmp(opt, "i") == 0) {
meillo@249 544 opt_i = TRUE;
meillo@249 545
meillo@249 546 } else if (strcmp(opt, "m") == 0) {
meillo@249 547 /* ignore -m (me too) switch (see man page) */
meillo@249 548
meillo@249 549 } else if (strcmp(opt, "Mrm") == 0) {
meillo@281 550 set_mode(MODE_MCMD);
meillo@249 551 M_cmd = "rm";
meillo@249 552
meillo@249 553 } else if (strcmp(opt, "odq") == 0) {
meillo@249 554 do_queue = TRUE;
meillo@249 555
meillo@249 556 } else if (strcmp(opt, "oi") == 0) {
meillo@257 557 opt_i = TRUE;
meillo@249 558
meillo@249 559 } else if (strncmp(opt, "o", 1) == 0) {
meillo@249 560 /* ignore all other -oXXX options */
meillo@249 561
meillo@249 562 } else if (strncmp(opt, "qo", 2) == 0) {
meillo@278 563 /* must be before the `q' check */
meillo@281 564 set_mode(MODE_RUNQUEUE);
meillo@249 565 do_runq = FALSE;
meillo@249 566 do_runq_online = TRUE;
meillo@249 567 /* can be NULL, then we use online detection method */
meillo@249 568 route_name = get_optarg(argv, &arg, opt+2);
meillo@249 569
meillo@249 570 } else if (strncmp(opt, "q", 1) == 0) {
meillo@249 571 /* must be after the `qo' check */
meillo@249 572 gchar *optarg;
meillo@249 573
meillo@251 574 optarg = get_optarg(argv, &arg, opt+1);
meillo@251 575 if (optarg) {
meillo@251 576 /* not just one single queue run but regular runs */
meillo@281 577 set_mode(MODE_DAEMON);
meillo@254 578 queue_interval = time_interval(optarg);
meillo@281 579 } else {
meillo@281 580 set_mode(MODE_RUNQUEUE);
meillo@281 581 do_runq = TRUE;
meillo@249 582 }
meillo@249 583
meillo@249 584 } else if (strcmp(opt, "t") == 0) {
meillo@249 585 opt_t = TRUE;
meillo@249 586
meillo@249 587 } else if (strcmp(opt, "v") == 0) {
meillo@249 588 do_verbose = TRUE;
meillo@249 589
meillo@10 590 } else {
meillo@249 591 fprintf(stderr, "unrecognized option `-%s'\n", opt);
meillo@262 592 exit(1);
meillo@10 593 }
meillo@0 594 }
meillo@0 595
meillo@281 596 if (!mta_mode && arg==argc && !opt_t) {
meillo@278 597 /*
meillo@278 598 In this case no rcpts can be found, thus no mail
meillo@278 599 can be sent, thus masqmail will always fail. We
meillo@278 600 rather do something better instead. This covers
meillo@278 601 also the case of calling masqmail without args.
meillo@278 602 */
meillo@281 603 mode_version();
meillo@281 604 exit(0);
meillo@259 605 }
meillo@259 606
meillo@10 607 if (mta_mode == MODE_VERSION) {
meillo@261 608 mode_version();
meillo@262 609 exit(0);
meillo@10 610 }
meillo@0 611
meillo@281 612 if (!mta_mode) {
meillo@281 613 mta_mode = MODE_ACCEPT;
meillo@281 614 }
meillo@281 615
meillo@10 616 /* initialize random generator */
meillo@10 617 srand(time(NULL));
meillo@10 618 /* ignore SIGPIPE signal */
meillo@10 619 signal(SIGPIPE, SIG_IGN);
meillo@0 620
meillo@73 621 /* close all possibly open file descriptors, except std{in,out,err} */
meillo@10 622 {
meillo@10 623 int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@0 624
meillo@251 625 if (max_fd <= 0) {
meillo@10 626 max_fd = 64;
meillo@251 627 }
meillo@251 628 for (i=3; i<max_fd; i++) {
meillo@10 629 close(i);
meillo@251 630 }
meillo@10 631 }
meillo@0 632
meillo@10 633 init_conf();
meillo@0 634
meillo@10 635 /* if we are not privileged, and the config file was changed we
meillo@10 636 implicetely set the the run_as_user flag and give up all
meillo@10 637 privileges.
meillo@0 638
meillo@10 639 So it is possible for a user to run his own daemon without
meillo@10 640 breaking security.
meillo@10 641 */
meillo@251 642 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
meillo@251 643 conf.run_as_user = TRUE;
meillo@251 644 seteuid(conf.orig_uid);
meillo@251 645 setegid(conf.orig_gid);
meillo@251 646 setuid(conf.orig_uid);
meillo@251 647 setgid(conf.orig_gid);
meillo@10 648 }
meillo@0 649
meillo@155 650 conf.log_dir = LOG_DIR;
meillo@155 651 logopen();
meillo@155 652 if (!read_conf(conf_file)) {
meillo@155 653 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
meillo@155 654 exit(5);
meillo@155 655 }
meillo@155 656 logclose();
meillo@0 657
meillo@251 658 if (do_queue) {
meillo@10 659 conf.do_queue = TRUE;
meillo@251 660 }
meillo@251 661 if (do_verbose) {
meillo@10 662 conf.do_verbose = TRUE;
meillo@251 663 }
meillo@251 664 if (debug_level >= 0) { /* if >= 0, it was given by argument */
meillo@10 665 conf.debug_level = debug_level;
meillo@251 666 }
meillo@0 667
meillo@46 668 /* It appears that changing to / ensures that we are never in
meillo@46 669 a directory which we cannot access. This situation could be
meillo@46 670 possible after changing identity.
meillo@46 671 Maybe we should only change to / if we not run as user, to
meillo@46 672 allow relative paths for log files in test setups for
meillo@46 673 instance.
meillo@46 674 */
meillo@10 675 chdir("/");
meillo@0 676
meillo@10 677 if (!conf.run_as_user) {
meillo@10 678 if (setgid(0) != 0) {
meillo@10 679 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
meillo@262 680 exit(1);
meillo@10 681 }
meillo@10 682 if (setuid(0) != 0) {
meillo@10 683 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
meillo@262 684 exit(1);
meillo@10 685 }
meillo@10 686 }
meillo@0 687
meillo@10 688 if (!logopen()) {
meillo@10 689 fprintf(stderr, "could not open log file\n");
meillo@262 690 exit(1);
meillo@10 691 }
meillo@0 692
meillo@10 693 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
meillo@0 694
meillo@10 695 DEBUG(5) {
meillo@10 696 gchar **str = argv;
meillo@10 697 debugf("args: \n");
meillo@10 698 while (*str) {
meillo@10 699 debugf("%s \n", *str);
meillo@10 700 str++;
meillo@10 701 }
meillo@10 702 }
meillo@10 703 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
meillo@0 704
meillo@10 705 if (f_address) {
meillo@10 706 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
meillo@10 707 g_free(f_address);
meillo@10 708 if (!return_path) {
meillo@10 709 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
meillo@262 710 exit(1);
meillo@10 711 }
meillo@10 712 }
meillo@10 713
meillo@10 714 switch (mta_mode) {
meillo@10 715 case MODE_DAEMON:
meillo@10 716 mode_daemon(do_listen, queue_interval, argv);
meillo@10 717 break;
meillo@251 718
meillo@10 719 case MODE_RUNQUEUE:
meillo@251 720 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
meillo@10 721 break;
meillo@10 722
meillo@10 723 case MODE_SMTP:
meillo@10 724 mode_smtp();
meillo@10 725 break;
meillo@10 726
meillo@10 727 case MODE_LIST:
meillo@10 728 queue_list();
meillo@10 729 break;
meillo@10 730
meillo@10 731 case MODE_BI:
meillo@262 732 exit(0);
meillo@10 733 break; /* well... */
meillo@10 734
meillo@10 735 case MODE_MCMD:
meillo@250 736 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
meillo@10 737 break;
meillo@10 738
meillo@10 739 case MODE_ACCEPT:
meillo@10 740 {
meillo@268 741 guint accept_flags = (opt_t ? ACC_RCPT_FROM_HEAD : 0)
meillo@110 742 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
meillo@10 743 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
meillo@258 744 exit(0);
meillo@10 745 }
meillo@10 746 break;
meillo@251 747
meillo@10 748 default:
meillo@10 749 fprintf(stderr, "unknown mode: %d\n", mta_mode);
meillo@10 750 break;
meillo@0 751 }
meillo@0 752
meillo@10 753 logclose();
meillo@0 754
meillo@263 755 exit(0);
meillo@0 756 }