masqmail

annotate src/masqmail.c @ 222:8cddc65765bd

added support for STARTTLS wrappers added the route config option `instant_helo' which causes masqmail, as SMTP client, not to wait for the server's 220 greeting. Instead if says EHLO right at once. You'll need this for STARTTLS wrappers that usually eat the greeting line.
author meillo@marmaro.de
date Fri, 23 Jul 2010 10:57:53 +0200
parents 4fd237550525
children 3c40f86d50e4
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@10 40 typedef enum _mta_mode {
meillo@10 41 MODE_ACCEPT = 0, /* accept message on stdin */
meillo@10 42 MODE_DAEMON, /* run as daemon */
meillo@10 43 MODE_RUNQUEUE, /* single queue run, online or offline */
meillo@10 44 MODE_SMTP, /* accept SMTP on stdin */
meillo@10 45 MODE_LIST, /* list queue */
meillo@10 46 MODE_MCMD, /* do queue manipulation */
meillo@10 47 MODE_VERSION, /* show version */
meillo@10 48 MODE_BI, /* fake ;-) */
meillo@10 49 MODE_NONE /* to prevent default MODE_ACCEPT */
meillo@10 50 } mta_mode;
meillo@0 51
meillo@0 52 char *pidfile = NULL;
meillo@0 53 volatile int sigterm_in_progress = 0;
meillo@0 54
meillo@10 55 static void
meillo@10 56 sigterm_handler(int sig)
meillo@0 57 {
meillo@10 58 if (sigterm_in_progress)
meillo@10 59 raise(sig);
meillo@10 60 sigterm_in_progress = 1;
meillo@0 61
meillo@10 62 if (pidfile) {
meillo@10 63 uid_t uid;
meillo@10 64 uid = seteuid(0);
meillo@10 65 if (unlink(pidfile) != 0)
meillo@10 66 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
meillo@10 67 seteuid(uid); /* we exit anyway after this, just to be sure */
meillo@10 68 }
meillo@0 69
meillo@10 70 signal(sig, SIG_DFL);
meillo@10 71 raise(sig);
meillo@0 72 }
meillo@0 73
meillo@10 74 #ifdef ENABLE_IDENT /* so far used for that only */
meillo@10 75 static gboolean
meillo@10 76 is_in_netlist(gchar * host, GList * netlist)
meillo@0 77 {
meillo@10 78 guint hostip = inet_addr(host);
meillo@10 79 struct in_addr addr;
meillo@0 80
meillo@10 81 addr.s_addr = hostip;
meillo@10 82 if (addr.s_addr != INADDR_NONE) {
meillo@10 83 GList *node;
meillo@10 84 foreach(netlist, node) {
meillo@10 85 struct in_addr *net = (struct in_addr *) (node->data);
meillo@10 86 if ((addr.s_addr & net->s_addr) == net->s_addr)
meillo@10 87 return TRUE;
meillo@10 88 }
meillo@10 89 }
meillo@10 90 return FALSE;
meillo@0 91 }
meillo@0 92 #endif
meillo@0 93
meillo@10 94 gchar*
meillo@10 95 get_optarg(char *argv[], gint argc, gint * argp, gint * pos)
meillo@0 96 {
meillo@10 97 if (argv[*argp][*pos])
meillo@10 98 return &(argv[*argp][*pos]);
meillo@10 99 else {
meillo@10 100 if (*argp + 1 < argc) {
meillo@10 101 if (argv[(*argp) + 1][0] != '-') {
meillo@10 102 (*argp)++;
meillo@10 103 *pos = 0;
meillo@10 104 return &(argv[*argp][*pos]);
meillo@10 105 }
meillo@10 106 }
meillo@10 107 }
meillo@10 108 return NULL;
meillo@0 109 }
meillo@0 110
meillo@10 111 gchar*
meillo@10 112 get_progname(gchar * arg0)
meillo@0 113 {
meillo@10 114 gchar *p = arg0 + strlen(arg0) - 1;
meillo@10 115 while (p > arg0) {
meillo@10 116 if (*p == '/')
meillo@10 117 return p + 1;
meillo@10 118 p--;
meillo@10 119 }
meillo@10 120 return p;
meillo@0 121 }
meillo@0 122
meillo@10 123 gboolean
meillo@10 124 write_pidfile(gchar * name)
meillo@0 125 {
meillo@10 126 FILE *fptr;
meillo@0 127
meillo@10 128 if ((fptr = fopen(name, "wt"))) {
meillo@10 129 fprintf(fptr, "%d\n", getpid());
meillo@10 130 fclose(fptr);
meillo@10 131 pidfile = strdup(name);
meillo@10 132 return TRUE;
meillo@10 133 }
meillo@10 134 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
meillo@10 135 return FALSE;
meillo@10 136 }
meillo@0 137
meillo@10 138 static void
meillo@10 139 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
meillo@10 140 {
meillo@10 141 guint pid;
meillo@0 142
meillo@10 143 /* daemon */
meillo@10 144 if (!conf.run_as_user) {
meillo@10 145 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@10 146 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
meillo@10 147 exit(EXIT_FAILURE);
meillo@10 148 }
meillo@10 149 }
meillo@0 150
meillo@74 151 /* reparent to init only if init is not already the parent */
meillo@74 152 if (getppid() != 1) {
meillo@74 153 if ((pid = fork()) > 0) {
meillo@74 154 exit(EXIT_SUCCESS);
meillo@74 155 } else if (pid < 0) {
meillo@208 156 logwrite(LOG_ALERT, "could not fork!\n");
meillo@74 157 exit(EXIT_FAILURE);
meillo@74 158 }
meillo@10 159 }
meillo@0 160
meillo@10 161 signal(SIGTERM, sigterm_handler);
meillo@10 162 write_pidfile(PIDFILEDIR "/masqmail.pid");
meillo@0 163
meillo@10 164 conf.do_verbose = FALSE;
meillo@10 165
meillo@72 166 /* closing and reopening the log ensures that it is open afterwards
meillo@72 167 because it is possible that the log is assigned to fd 1 and gets
meillo@72 168 thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 169 */
meillo@72 170 logclose();
meillo@10 171 fclose(stdin);
meillo@10 172 fclose(stdout);
meillo@10 173 fclose(stderr);
meillo@72 174 logopen();
meillo@10 175
meillo@208 176 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
meillo@10 177 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
meillo@0 178 }
meillo@0 179
meillo@10 180 static void
meillo@10 181 mode_smtp()
meillo@0 182 {
meillo@10 183 /* accept smtp message on stdin */
meillo@10 184 /* write responses to stderr. */
meillo@0 185
meillo@10 186 struct sockaddr_in saddr;
meillo@10 187 gchar *peername = NULL;
meillo@10 188 int dummy = sizeof(saddr);
meillo@0 189
meillo@10 190 conf.do_verbose = FALSE;
meillo@0 191
meillo@10 192 if (!conf.run_as_user) {
meillo@10 193 seteuid(conf.orig_uid);
meillo@10 194 setegid(conf.orig_gid);
meillo@10 195 }
meillo@0 196
meillo@10 197 DEBUG(5) debugf("accepting smtp message on stdin\n");
meillo@0 198
meillo@10 199 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
meillo@10 200 peername = g_strdup(inet_ntoa(saddr.sin_addr));
meillo@10 201 } else if (errno != ENOTSOCK)
meillo@10 202 exit(EXIT_FAILURE);
meillo@0 203
meillo@10 204 smtp_in(stdin, stderr, peername, NULL);
meillo@0 205 }
meillo@0 206
meillo@10 207 static void
meillo@10 208 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
meillo@0 209 {
meillo@10 210 /* accept message on stdin */
meillo@10 211 accept_error err;
meillo@10 212 message *msg = create_message();
meillo@10 213 gint i;
meillo@0 214
meillo@83 215 if (return_path && !is_privileged_user(conf.orig_uid)) {
meillo@96 216 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
meillo@83 217 exit(EXIT_FAILURE);
meillo@10 218 }
meillo@0 219
meillo@10 220 if (!conf.run_as_user) {
meillo@10 221 seteuid(conf.orig_uid);
meillo@10 222 setegid(conf.orig_gid);
meillo@10 223 }
meillo@0 224
meillo@10 225 DEBUG(5) debugf("accepting message on stdin\n");
meillo@0 226
meillo@10 227 msg->received_prot = PROT_LOCAL;
meillo@10 228 for (i = 0; i < addr_cnt; i++) {
meillo@10 229 if (addresses[i][0] != '|')
meillo@10 230 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
meillo@10 231 else {
meillo@10 232 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
meillo@10 233 exit(EXIT_FAILURE);
meillo@10 234 }
meillo@10 235 }
meillo@0 236
meillo@10 237 /* -f option */
meillo@10 238 msg->return_path = return_path;
meillo@0 239
meillo@10 240 /* -F option */
meillo@10 241 msg->full_sender_name = full_sender_name;
meillo@0 242
meillo@10 243 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
meillo@10 244 if (spool_write(msg, TRUE)) {
meillo@10 245 pid_t pid;
meillo@10 246 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
meillo@0 247
meillo@10 248 if (!conf.do_queue) {
meillo@10 249 if ((pid = fork()) == 0) {
meillo@10 250 conf.do_verbose = FALSE;
meillo@10 251 fclose(stdin);
meillo@10 252 fclose(stdout);
meillo@10 253 fclose(stderr);
meillo@10 254 if (deliver(msg)) {
meillo@10 255 exit(EXIT_SUCCESS);
meillo@10 256 } else
meillo@10 257 exit(EXIT_FAILURE);
meillo@10 258 } else if (pid < 0) {
meillo@208 259 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
meillo@10 260 }
meillo@10 261 }
meillo@10 262 } else {
meillo@10 263 fprintf(stderr, "Could not write spool file\n");
meillo@10 264 exit(EXIT_FAILURE);
meillo@10 265 }
meillo@10 266 } else {
meillo@10 267 switch (err) {
meillo@10 268 case AERR_EOF:
meillo@10 269 fprintf(stderr, "unexpected EOF.\n");
meillo@10 270 exit(EXIT_FAILURE);
meillo@10 271 case AERR_NORCPT:
meillo@10 272 fprintf(stderr, "no recipients.\n");
meillo@10 273 exit(EXIT_FAILURE);
meillo@117 274 case AERR_SIZE:
meillo@117 275 fprintf(stderr, "max message size exceeded.\n");
meillo@117 276 exit(EXIT_FAILURE);
meillo@10 277 default:
meillo@10 278 /* should never happen: */
meillo@10 279 fprintf(stderr, "Unknown error (%d)\r\n", err);
meillo@10 280 exit(EXIT_FAILURE);
meillo@10 281 }
meillo@10 282 exit(EXIT_FAILURE);
meillo@0 283 }
meillo@0 284 }
meillo@0 285
meillo@0 286 int
meillo@0 287 main(int argc, char *argv[])
meillo@0 288 {
meillo@10 289 /* cmd line flags */
meillo@10 290 gchar *conf_file = CONF_FILE;
meillo@10 291 gint arg = 1;
meillo@0 292
meillo@10 293 gboolean do_listen = FALSE;
meillo@10 294 gboolean do_runq = FALSE;
meillo@10 295 gboolean do_runq_online = FALSE;
meillo@0 296
meillo@10 297 gboolean do_queue = FALSE;
meillo@0 298
meillo@10 299 gboolean do_verbose = FALSE;
meillo@10 300 gint debug_level = -1;
meillo@0 301
meillo@10 302 mta_mode mta_mode = MODE_ACCEPT;
meillo@0 303
meillo@10 304 gint queue_interval = 0;
meillo@10 305 gboolean opt_t = FALSE;
meillo@10 306 gboolean opt_i = FALSE;
meillo@10 307 gboolean opt_odb = FALSE;
meillo@10 308 gboolean opt_oem = FALSE;
meillo@10 309 gboolean exit_failure = FALSE;
meillo@0 310
meillo@10 311 gchar *M_cmd = NULL;
meillo@0 312
meillo@10 313 gint exit_code = EXIT_SUCCESS;
meillo@10 314 gchar *route_name = NULL;
meillo@10 315 gchar *progname;
meillo@10 316 gchar *f_address = NULL;
meillo@10 317 gchar *full_sender_name = NULL;
meillo@10 318 address *return_path = NULL; /* may be changed by -f option */
meillo@0 319
meillo@10 320 progname = get_progname(argv[0]);
meillo@0 321
meillo@10 322 if (strcmp(progname, "mailq") == 0) {
meillo@10 323 mta_mode = MODE_LIST;
meillo@10 324 } else if (strcmp(progname, "mailrm") == 0) {
meillo@10 325 mta_mode = MODE_MCMD;
meillo@10 326 M_cmd = "rm";
meillo@10 327 } else if (strcmp(progname, "runq") == 0) {
meillo@10 328 mta_mode = MODE_RUNQUEUE;
meillo@10 329 do_runq = TRUE;
meillo@10 330 } else if (strcmp(progname, "rmail") == 0) {
meillo@89 331 /* the `rmail' alias should probably be removed now
meillo@89 332 that we have the rmail script. But let's keep it
meillo@89 333 for some while for compatibility. 2010-06-19 */
meillo@10 334 mta_mode = MODE_ACCEPT;
meillo@10 335 opt_i = TRUE;
meillo@10 336 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
meillo@10 337 mta_mode = MODE_SMTP;
meillo@10 338 }
meillo@0 339
meillo@10 340 /* parse cmd line */
meillo@10 341 while (arg < argc) {
meillo@10 342 gint pos = 0;
meillo@10 343 if ((argv[arg][pos] == '-') && (argv[arg][pos + 1] != '-')) {
meillo@10 344 pos++;
meillo@10 345 switch (argv[arg][pos++]) {
meillo@10 346 case 'b':
meillo@10 347 switch (argv[arg][pos++]) {
meillo@10 348 case 'd':
meillo@10 349 do_listen = TRUE;
meillo@10 350 mta_mode = MODE_DAEMON;
meillo@10 351 break;
meillo@10 352 case 'i':
meillo@10 353 /* ignored */
meillo@10 354 mta_mode = MODE_BI;
meillo@10 355 break;
meillo@10 356 case 's':
meillo@10 357 mta_mode = MODE_SMTP;
meillo@10 358 break;
meillo@10 359 case 'p':
meillo@10 360 mta_mode = MODE_LIST;
meillo@10 361 break;
meillo@10 362 case 'V':
meillo@10 363 mta_mode = MODE_VERSION;
meillo@10 364 break;
meillo@10 365 default:
meillo@10 366 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 367 exit(EXIT_FAILURE);
meillo@10 368 }
meillo@10 369 break;
meillo@10 370 case 'B':
meillo@10 371 /* we ignore this and throw the argument away */
meillo@10 372 get_optarg(argv, argc, &arg, &pos);
meillo@10 373 break;
meillo@10 374 case 'C':
meillo@10 375 if (!(conf_file = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 376 fprintf(stderr, "-C requires a filename as argument.\n");
meillo@10 377 exit(EXIT_FAILURE);
meillo@10 378 }
meillo@10 379 break;
meillo@10 380 case 'F':
meillo@10 381 {
meillo@10 382 full_sender_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 383 if (!full_sender_name) {
meillo@10 384 fprintf(stderr, "-F requires a name as an argument\n");
meillo@10 385 exit(EXIT_FAILURE);
meillo@10 386 }
meillo@10 387 }
meillo@10 388 break;
meillo@10 389 case 'd':
meillo@10 390 if (getuid() == 0) {
meillo@10 391 char *lvl = get_optarg(argv, argc, &arg, &pos);
meillo@10 392 if (lvl)
meillo@10 393 debug_level = atoi(lvl);
meillo@10 394 else {
meillo@10 395 fprintf(stderr, "-d requires a number as an argument.\n");
meillo@10 396 exit(EXIT_FAILURE);
meillo@10 397 }
meillo@10 398 } else {
meillo@10 399 fprintf(stderr, "only root may set the debug level.\n");
meillo@10 400 exit(EXIT_FAILURE);
meillo@10 401 }
meillo@10 402 break;
meillo@10 403 case 'f':
meillo@10 404 /* set return path */
meillo@10 405 {
meillo@10 406 gchar *address;
meillo@10 407 address = get_optarg(argv, argc, &arg, &pos);
meillo@10 408 if (address) {
meillo@10 409 f_address = g_strdup(address);
meillo@10 410 } else {
meillo@10 411 fprintf(stderr, "-f requires an address as an argument\n");
meillo@10 412 exit(EXIT_FAILURE);
meillo@10 413 }
meillo@10 414 }
meillo@10 415 break;
meillo@10 416 case 'i':
meillo@10 417 if (argv[arg][pos] == 0) {
meillo@10 418 opt_i = TRUE;
meillo@10 419 exit_failure = FALSE; /* may override -oem */
meillo@10 420 } else {
meillo@10 421 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 422 exit(EXIT_FAILURE);
meillo@10 423 }
meillo@10 424 break;
meillo@10 425 case 'M':
meillo@10 426 {
meillo@10 427 mta_mode = MODE_MCMD;
meillo@10 428 M_cmd = g_strdup(&(argv[arg][pos]));
meillo@10 429 }
meillo@10 430 break;
meillo@10 431 case 'o':
meillo@10 432 switch (argv[arg][pos++]) {
meillo@10 433 case 'e':
meillo@10 434 if (argv[arg][pos++] == 'm') /* -oem */
meillo@10 435 if (!opt_i)
meillo@10 436 exit_failure = TRUE;
meillo@10 437 opt_oem = TRUE;
meillo@10 438 break;
meillo@10 439 case 'd':
meillo@10 440 if (argv[arg][pos] == 'b') /* -odb */
meillo@10 441 opt_odb = TRUE;
meillo@10 442 else if (argv[arg][pos] == 'q') /* -odq */
meillo@10 443 do_queue = TRUE;
meillo@10 444 break;
meillo@10 445 case 'i':
meillo@10 446 opt_i = TRUE;
meillo@10 447 exit_failure = FALSE; /* may override -oem */
meillo@10 448 break;
meillo@10 449 }
meillo@10 450 break;
meillo@10 451
meillo@10 452 case 'q':
meillo@10 453 {
meillo@10 454 gchar *optarg;
meillo@10 455
meillo@10 456 do_runq = TRUE;
meillo@10 457 mta_mode = MODE_RUNQUEUE;
meillo@10 458 if (argv[arg][pos] == 'o') {
meillo@10 459 pos++;
meillo@10 460 do_runq = FALSE;
meillo@10 461 do_runq_online = TRUE;
meillo@10 462 /* can be NULL, then we use online detection method */
meillo@10 463 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 464 } else
meillo@10 465 if ((optarg = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 466 mta_mode = MODE_DAEMON;
meillo@10 467 queue_interval = time_interval(optarg, &pos);
meillo@10 468 }
meillo@10 469 }
meillo@10 470 break;
meillo@10 471 case 't':
meillo@10 472 if (argv[arg][pos] == 0) {
meillo@10 473 opt_t = TRUE;
meillo@10 474 } else {
meillo@10 475 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 476 exit(EXIT_FAILURE);
meillo@10 477 }
meillo@10 478 break;
meillo@10 479 case 'v':
meillo@10 480 do_verbose = TRUE;
meillo@10 481 break;
meillo@10 482 default:
meillo@10 483 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 484 exit(EXIT_FAILURE);
meillo@10 485 }
meillo@10 486 } else {
meillo@10 487 if (argv[arg][pos + 1] == '-') {
meillo@10 488 if (argv[arg][pos + 2] != '\0') {
meillo@10 489 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 490 exit(EXIT_FAILURE);
meillo@10 491 }
meillo@10 492 arg++;
meillo@10 493 }
meillo@10 494 break;
meillo@10 495 }
meillo@10 496 arg++;
meillo@0 497 }
meillo@0 498
meillo@10 499 if (mta_mode == MODE_VERSION) {
meillo@12 500 gchar *with_resolver = "";
meillo@12 501 gchar *with_auth = "";
meillo@12 502 gchar *with_ident = "";
meillo@0 503
meillo@0 504 #ifdef ENABLE_RESOLVER
meillo@10 505 with_resolver = " +resolver";
meillo@0 506 #endif
meillo@0 507 #ifdef ENABLE_AUTH
meillo@10 508 with_auth = " +auth";
meillo@0 509 #endif
meillo@0 510 #ifdef ENABLE_IDENT
meillo@10 511 with_ident = " +ident";
meillo@0 512 #endif
meillo@0 513
meillo@205 514 printf("%s %s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth, with_ident);
meillo@0 515
meillo@10 516 exit(EXIT_SUCCESS);
meillo@10 517 }
meillo@0 518
meillo@10 519 /* initialize random generator */
meillo@10 520 srand(time(NULL));
meillo@10 521 /* ignore SIGPIPE signal */
meillo@10 522 signal(SIGPIPE, SIG_IGN);
meillo@0 523
meillo@73 524 /* close all possibly open file descriptors, except std{in,out,err} */
meillo@10 525 {
meillo@10 526 int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@0 527
meillo@10 528 if (max_fd <= 0)
meillo@10 529 max_fd = 64;
meillo@10 530 for (i = 3; i < max_fd; i++)
meillo@10 531 close(i);
meillo@10 532 }
meillo@0 533
meillo@10 534 init_conf();
meillo@0 535
meillo@10 536 /* if we are not privileged, and the config file was changed we
meillo@10 537 implicetely set the the run_as_user flag and give up all
meillo@10 538 privileges.
meillo@0 539
meillo@10 540 So it is possible for a user to run his own daemon without
meillo@10 541 breaking security.
meillo@10 542 */
meillo@10 543 if (strcmp(conf_file, CONF_FILE) != 0) {
meillo@10 544 if (conf.orig_uid != 0) {
meillo@10 545 conf.run_as_user = TRUE;
meillo@10 546 seteuid(conf.orig_uid);
meillo@10 547 setegid(conf.orig_gid);
meillo@10 548 setuid(conf.orig_uid);
meillo@10 549 setgid(conf.orig_gid);
meillo@10 550 }
meillo@10 551 }
meillo@0 552
meillo@155 553 conf.log_dir = LOG_DIR;
meillo@155 554 logopen();
meillo@155 555 if (!read_conf(conf_file)) {
meillo@155 556 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
meillo@155 557 exit(5);
meillo@155 558 }
meillo@155 559 logclose();
meillo@0 560
meillo@10 561 if (do_queue)
meillo@10 562 conf.do_queue = TRUE;
meillo@10 563 if (do_verbose)
meillo@10 564 conf.do_verbose = TRUE;
meillo@10 565 if (debug_level >= 0) /* if >= 0, it was given by argument */
meillo@10 566 conf.debug_level = debug_level;
meillo@0 567
meillo@46 568 /* It appears that changing to / ensures that we are never in
meillo@46 569 a directory which we cannot access. This situation could be
meillo@46 570 possible after changing identity.
meillo@46 571 Maybe we should only change to / if we not run as user, to
meillo@46 572 allow relative paths for log files in test setups for
meillo@46 573 instance.
meillo@46 574 */
meillo@10 575 chdir("/");
meillo@0 576
meillo@10 577 if (!conf.run_as_user) {
meillo@10 578 if (setgid(0) != 0) {
meillo@10 579 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
meillo@10 580 exit(EXIT_FAILURE);
meillo@10 581 }
meillo@10 582 if (setuid(0) != 0) {
meillo@10 583 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
meillo@10 584 exit(EXIT_FAILURE);
meillo@10 585 }
meillo@10 586 }
meillo@0 587
meillo@10 588 if (!logopen()) {
meillo@10 589 fprintf(stderr, "could not open log file\n");
meillo@10 590 exit(EXIT_FAILURE);
meillo@10 591 }
meillo@0 592
meillo@10 593 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
meillo@0 594
meillo@10 595 DEBUG(5) {
meillo@10 596 gchar **str = argv;
meillo@10 597 debugf("args: \n");
meillo@10 598 while (*str) {
meillo@10 599 debugf("%s \n", *str);
meillo@10 600 str++;
meillo@10 601 }
meillo@10 602 }
meillo@10 603 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
meillo@0 604
meillo@10 605 if (f_address) {
meillo@10 606 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
meillo@10 607 g_free(f_address);
meillo@10 608 if (!return_path) {
meillo@10 609 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
meillo@10 610 exit(EXIT_FAILURE);
meillo@10 611 }
meillo@10 612 }
meillo@10 613
meillo@10 614 switch (mta_mode) {
meillo@10 615 case MODE_DAEMON:
meillo@10 616 mode_daemon(do_listen, queue_interval, argv);
meillo@10 617 break;
meillo@10 618 case MODE_RUNQUEUE:
meillo@10 619 {
meillo@10 620 /* queue runs */
meillo@10 621 set_identity(conf.orig_uid, "queue run");
meillo@0 622
meillo@10 623 if (do_runq)
meillo@10 624 exit_code = queue_run() ? EXIT_SUCCESS : EXIT_FAILURE;
meillo@10 625
meillo@10 626 if (do_runq_online) {
meillo@10 627 if (route_name != NULL) {
meillo@10 628 conf.online_detect = g_strdup("argument");
meillo@10 629 set_online_name(route_name);
meillo@10 630 }
meillo@10 631 exit_code =
meillo@10 632 queue_run_online() ? EXIT_SUCCESS : EXIT_FAILURE;
meillo@10 633 }
meillo@10 634 }
meillo@10 635 break;
meillo@10 636
meillo@10 637 case MODE_SMTP:
meillo@10 638 mode_smtp();
meillo@10 639 break;
meillo@10 640
meillo@10 641 case MODE_LIST:
meillo@10 642 queue_list();
meillo@10 643 break;
meillo@10 644
meillo@10 645 case MODE_BI:
meillo@10 646 exit(EXIT_SUCCESS);
meillo@10 647 break; /* well... */
meillo@10 648
meillo@10 649 case MODE_MCMD:
meillo@10 650 if (strcmp(M_cmd, "rm") == 0) {
meillo@10 651 gboolean ok = FALSE;
meillo@10 652
meillo@10 653 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
meillo@10 654
meillo@10 655 if (is_privileged_user(conf.orig_uid)) {
meillo@10 656 for (; arg < argc; arg++) {
meillo@10 657 if (queue_delete(argv[arg]))
meillo@10 658 ok = TRUE;
meillo@10 659 }
meillo@10 660 } else {
meillo@10 661 struct passwd *pw = getpwuid(conf.orig_uid);
meillo@10 662 if (pw) {
meillo@10 663 for (; arg < argc; arg++) {
meillo@10 664 message *msg = msg_spool_read(argv[arg], FALSE);
meillo@10 665 #ifdef ENABLE_IDENT
meillo@10 666 if (((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL))
meillo@10 667 || is_in_netlist(msg->received_host, conf.ident_trusted_nets)) {
meillo@10 668 #else
meillo@10 669 if ((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL)) {
meillo@10 670 #endif
meillo@10 671 if (msg->ident) {
meillo@10 672 if (strcmp(pw->pw_name, msg->ident) == 0) {
meillo@10 673 if (queue_delete(argv[arg]))
meillo@10 674 ok = TRUE;
meillo@10 675 } else {
meillo@10 676 fprintf(stderr, "you do not own message id %s\n", argv[arg]);
meillo@10 677 }
meillo@10 678 } else
meillo@10 679 fprintf(stderr, "message %s does not have an ident.\n", argv[arg]);
meillo@10 680 } else {
meillo@10 681 fprintf(stderr, "message %s was not received locally or from a trusted network.\n", argv[arg]);
meillo@10 682 }
meillo@10 683 }
meillo@10 684 } else {
meillo@10 685 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n", conf.orig_uid, strerror(errno));
meillo@10 686 }
meillo@10 687 }
meillo@10 688 exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
meillo@10 689 } else {
meillo@10 690 fprintf(stderr, "unknown command %s\n", M_cmd);
meillo@10 691 exit(EXIT_FAILURE);
meillo@10 692 }
meillo@10 693 break;
meillo@10 694
meillo@10 695 case MODE_ACCEPT:
meillo@10 696 {
meillo@107 697 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
meillo@110 698 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
meillo@10 699 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
meillo@10 700 exit(exit_failure ? EXIT_FAILURE : EXIT_SUCCESS);
meillo@10 701 }
meillo@10 702 break;
meillo@10 703 case MODE_NONE:
meillo@10 704 break;
meillo@10 705 default:
meillo@10 706 fprintf(stderr, "unknown mode: %d\n", mta_mode);
meillo@10 707 break;
meillo@0 708 }
meillo@0 709
meillo@10 710 logclose();
meillo@0 711
meillo@10 712 exit(exit_code);
meillo@0 713 }