1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "dfcompat.h" 36 37 #include <sys/param.h> 38 #include <sys/types.h> 39 #include <sys/queue.h> 40 #include <sys/stat.h> 41 #include <sys/time.h> 42 #include <sys/wait.h> 43 44 #include <dirent.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <inttypes.h> 49 #include <paths.h> 50 #include <pwd.h> 51 #include <signal.h> 52 #include <stdarg.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <syslog.h> 57 #include <unistd.h> 58 59 #include "dma.h" 60 61 62 static void deliver(struct qitem *); 63 64 struct aliases aliases = LIST_HEAD_INITIALIZER(aliases); 65 struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs); 66 struct authusers authusers = LIST_HEAD_INITIALIZER(authusers); 67 char username[USERNAME_SIZE]; 68 uid_t useruid; 69 const char *logident_base; 70 char errmsg[ERRMSG_SIZE]; 71 72 static int daemonize = 1; 73 static int doqueue = 0; 74 75 struct config config = { 76 .smarthost = NULL, 77 .port = 25, 78 .aliases = "/etc/aliases", 79 .spooldir = "/var/spool/dma", 80 .authpath = NULL, 81 .certfile = NULL, 82 .features = 0, 83 .mailname = NULL, 84 .masquerade_host = NULL, 85 .masquerade_user = NULL, 86 }; 87 88 89 static void 90 sighup_handler(int signo) 91 { 92 (void)signo; /* so that gcc doesn't complain */ 93 } 94 95 static char * 96 set_from(struct queue *queue, const char *osender) 97 { 98 const char *addr; 99 char *sender; 100 101 if (osender) { 102 addr = osender; 103 } else if (getenv("EMAIL") != NULL) { 104 addr = getenv("EMAIL"); 105 } else { 106 if (config.masquerade_user) 107 addr = config.masquerade_user; 108 else 109 addr = username; 110 } 111 112 if (!strchr(addr, '@')) { 113 const char *from_host = hostname(); 114 115 if (config.masquerade_host) 116 from_host = config.masquerade_host; 117 118 if (asprintf(&sender, "%s@%s", addr, from_host) <= 0) 119 return (NULL); 120 } else { 121 sender = strdup(addr); 122 if (sender == NULL) 123 return (NULL); 124 } 125 126 if (strchr(sender, '\n') != NULL) { 127 errno = EINVAL; 128 return (NULL); 129 } 130 131 queue->sender = sender; 132 return (sender); 133 } 134 135 static int 136 read_aliases(void) 137 { 138 yyin = fopen(config.aliases, "r"); 139 if (yyin == NULL) { 140 /* 141 * Non-existing aliases file is not a fatal error 142 */ 143 if (errno == ENOENT) 144 return (0); 145 /* Other problems are. */ 146 return (-1); 147 } 148 if (yyparse()) 149 return (-1); /* fatal error, probably malloc() */ 150 fclose(yyin); 151 return (0); 152 } 153 154 static int 155 do_alias(struct queue *queue, const char *addr) 156 { 157 struct alias *al; 158 struct stritem *sit; 159 int aliased = 0; 160 161 LIST_FOREACH(al, &aliases, next) { 162 if (strcmp(al->alias, addr) != 0) 163 continue; 164 SLIST_FOREACH(sit, &al->dests, next) { 165 if (add_recp(queue, sit->str, EXPAND_ADDR) != 0) 166 return (-1); 167 } 168 aliased = 1; 169 } 170 171 return (aliased); 172 } 173 174 int 175 add_recp(struct queue *queue, const char *str, int expand) 176 { 177 struct qitem *it, *tit; 178 struct passwd *pw; 179 char *host; 180 int aliased = 0; 181 182 it = calloc(1, sizeof(*it)); 183 if (it == NULL) 184 return (-1); 185 it->addr = strdup(str); 186 if (it->addr == NULL) 187 return (-1); 188 189 it->sender = queue->sender; 190 host = strrchr(it->addr, '@'); 191 if (host != NULL && 192 (strcmp(host + 1, hostname()) == 0 || 193 strcmp(host + 1, "localhost") == 0)) { 194 *host = 0; 195 } 196 LIST_FOREACH(tit, &queue->queue, next) { 197 /* weed out duplicate dests */ 198 if (strcmp(tit->addr, it->addr) == 0) { 199 free(it->addr); 200 free(it); 201 return (0); 202 } 203 } 204 LIST_INSERT_HEAD(&queue->queue, it, next); 205 206 /** 207 * Do local delivery if there is no @. 208 * Do not do local delivery when NULLCLIENT is set. 209 */ 210 if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) { 211 it->remote = 0; 212 if (expand) { 213 aliased = do_alias(queue, it->addr); 214 if (!aliased && expand == EXPAND_WILDCARD) 215 aliased = do_alias(queue, "*"); 216 if (aliased < 0) 217 return (-1); 218 if (aliased) { 219 LIST_REMOVE(it, next); 220 } else { 221 /* Local destination, check */ 222 pw = getpwnam(it->addr); 223 if (pw == NULL) 224 goto out; 225 /* XXX read .forward */ 226 endpwent(); 227 } 228 } 229 } else { 230 it->remote = 1; 231 } 232 233 return (0); 234 235 out: 236 free(it->addr); 237 free(it); 238 return (-1); 239 } 240 241 static struct qitem * 242 go_background(struct queue *queue) 243 { 244 struct sigaction sa; 245 struct qitem *it; 246 pid_t pid; 247 248 if (daemonize && daemon(0, 0) != 0) { 249 syslog(LOG_ERR, "can not daemonize: %m"); 250 exit(1); 251 } 252 daemonize = 0; 253 254 bzero(&sa, sizeof(sa)); 255 sa.sa_handler = SIG_IGN; 256 sigaction(SIGCHLD, &sa, NULL); 257 258 LIST_FOREACH(it, &queue->queue, next) { 259 /* No need to fork for the last dest */ 260 if (LIST_NEXT(it, next) == NULL) 261 goto retit; 262 263 pid = fork(); 264 switch (pid) { 265 case -1: 266 syslog(LOG_ERR, "can not fork: %m"); 267 exit(1); 268 break; 269 270 case 0: 271 /* 272 * Child: 273 * 274 * return and deliver mail 275 */ 276 retit: 277 /* 278 * If necessary, acquire the queue and * mail files. 279 * If this fails, we probably were raced by another 280 * process. It is okay to be raced if we're supposed 281 * to flush the queue. 282 */ 283 setlogident("%s", it->queueid); 284 switch (acquirespool(it)) { 285 case 0: 286 break; 287 case 1: 288 if (doqueue) 289 exit(0); 290 syslog(LOG_WARNING, "could not lock queue file"); 291 exit(1); 292 default: 293 exit(1); 294 } 295 dropspool(queue, it); 296 return (it); 297 298 default: 299 /* 300 * Parent: 301 * 302 * fork next child 303 */ 304 break; 305 } 306 } 307 308 syslog(LOG_CRIT, "reached dead code"); 309 exit(1); 310 } 311 312 static void 313 deliver(struct qitem *it) 314 { 315 int error; 316 unsigned int backoff = MIN_RETRY, slept; 317 struct timeval now; 318 struct stat st; 319 320 snprintf(errmsg, sizeof(errmsg), "unknown bounce reason"); 321 322 retry: 323 syslog(LOG_INFO, "trying delivery"); 324 325 if (it->remote) 326 error = deliver_remote(it); 327 else 328 error = deliver_local(it); 329 330 switch (error) { 331 case 0: 332 delqueue(it); 333 syslog(LOG_INFO, "delivery successful"); 334 exit(0); 335 336 case 1: 337 if (stat(it->queuefn, &st) != 0) { 338 syslog(LOG_ERR, "lost queue file `%s'", it->queuefn); 339 exit(1); 340 } 341 if (gettimeofday(&now, NULL) == 0 && 342 (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) { 343 snprintf(errmsg, sizeof(errmsg), 344 "Could not deliver for the last %d seconds. Giving up.", 345 MAX_TIMEOUT); 346 goto bounce; 347 } 348 for (slept = 0; slept < backoff;) { 349 slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT); 350 if (flushqueue_since(slept)) { 351 backoff = MIN_RETRY; 352 goto retry; 353 } 354 } 355 if (slept >= backoff) { 356 /* pick the next backoff between [1.5, 2.5) times backoff */ 357 backoff = backoff + backoff / 2 + random() % backoff; 358 if (backoff > MAX_RETRY) 359 backoff = MAX_RETRY; 360 } 361 goto retry; 362 363 case -1: 364 default: 365 break; 366 } 367 368 bounce: 369 bounce(it, errmsg); 370 /* NOTREACHED */ 371 } 372 373 void 374 run_queue(struct queue *queue) 375 { 376 struct qitem *it; 377 378 if (LIST_EMPTY(&queue->queue)) 379 return; 380 381 it = go_background(queue); 382 deliver(it); 383 /* NOTREACHED */ 384 } 385 386 static void 387 show_queue(struct queue *queue) 388 { 389 struct qitem *it; 390 int locked = 0; /* XXX */ 391 392 if (LIST_EMPTY(&queue->queue)) { 393 printf("Mail queue is empty\n"); 394 return; 395 } 396 397 LIST_FOREACH(it, &queue->queue, next) { 398 printf("ID\t: %s%s\n" 399 "From\t: %s\n" 400 "To\t: %s\n", 401 it->queueid, 402 locked ? "*" : "", 403 it->sender, it->addr); 404 405 if (LIST_NEXT(it, next) != NULL) 406 printf("--\n"); 407 } 408 } 409 410 /* 411 * TODO: 412 * 413 * - alias processing 414 * - use group permissions 415 * - proper sysexit codes 416 */ 417 418 int 419 main(int argc, char **argv) 420 { 421 struct sigaction act; 422 char *sender = NULL; 423 struct queue queue; 424 int i, ch; 425 int nodot = 0, showq = 0, queue_only = 0; 426 int recp_from_header = 0; 427 428 set_username(); 429 430 /* 431 * We never run as root. If called by root, drop permissions 432 * to the mail user. 433 */ 434 if (geteuid() == 0 || getuid() == 0) { 435 struct passwd *pw; 436 437 errno = 0; 438 pw = getpwnam(DMA_ROOT_USER); 439 if (pw == NULL) { 440 if (errno == 0) 441 errx(1, "user '%s' not found", DMA_ROOT_USER); 442 else 443 err(1, "cannot drop root privileges"); 444 } 445 446 if (setuid(pw->pw_uid) != 0) 447 err(1, "cannot drop root privileges"); 448 449 if (geteuid() == 0 || getuid() == 0) 450 errx(1, "cannot drop root privileges"); 451 } 452 453 atexit(deltmp); 454 init_random(); 455 456 bzero(&queue, sizeof(queue)); 457 LIST_INIT(&queue.queue); 458 459 if (strcmp(argv[0], "mailq") == 0) { 460 argv++; argc--; 461 showq = 1; 462 if (argc != 0) 463 errx(1, "invalid arguments"); 464 goto skipopts; 465 } else if (strcmp(argv[0], "newaliases") == 0) { 466 logident_base = "dma"; 467 setlogident("%s", logident_base); 468 469 if (read_aliases() != 0) 470 errx(1, "could not parse aliases file `%s'", config.aliases); 471 exit(0); 472 } 473 474 opterr = 0; 475 while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) { 476 switch (ch) { 477 case 'A': 478 /* -AX is being ignored, except for -A{c,m} */ 479 if (optarg[0] == 'c' || optarg[0] == 'm') { 480 break; 481 } 482 /* else FALLTRHOUGH */ 483 case 'b': 484 /* -bX is being ignored, except for -bp */ 485 if (optarg[0] == 'p') { 486 showq = 1; 487 break; 488 } else if (optarg[0] == 'q') { 489 queue_only = 1; 490 break; 491 } 492 /* else FALLTRHOUGH */ 493 case 'D': 494 daemonize = 0; 495 break; 496 case 'L': 497 logident_base = optarg; 498 break; 499 case 'f': 500 case 'r': 501 sender = optarg; 502 break; 503 504 case 't': 505 recp_from_header = 1; 506 break; 507 508 case 'o': 509 /* -oX is being ignored, except for -oi */ 510 if (optarg[0] != 'i') 511 break; 512 /* else FALLTRHOUGH */ 513 case 'O': 514 break; 515 case 'i': 516 nodot = 1; 517 break; 518 519 case 'q': 520 /* Don't let getopt slup up other arguments */ 521 if (optarg && *optarg == '-') 522 optind--; 523 doqueue = 1; 524 break; 525 526 /* Ignored options */ 527 case 'B': 528 case 'C': 529 case 'd': 530 case 'F': 531 case 'h': 532 case 'N': 533 case 'n': 534 case 'R': 535 case 'U': 536 case 'V': 537 case 'v': 538 case 'X': 539 break; 540 541 case ':': 542 if (optopt == 'q') { 543 doqueue = 1; 544 break; 545 } 546 /* FALLTHROUGH */ 547 548 default: 549 fprintf(stderr, "invalid argument: `-%c'\n", optopt); 550 exit(1); 551 } 552 } 553 argc -= optind; 554 argv += optind; 555 opterr = 1; 556 557 if (argc != 0 && (showq || doqueue)) 558 errx(1, "sending mail and queue operations are mutually exclusive"); 559 560 if (showq + doqueue > 1) 561 errx(1, "conflicting queue operations"); 562 563 skipopts: 564 if (logident_base == NULL) 565 logident_base = "dma"; 566 setlogident("%s", logident_base); 567 568 act.sa_handler = sighup_handler; 569 act.sa_flags = 0; 570 sigemptyset(&act.sa_mask); 571 if (sigaction(SIGHUP, &act, NULL) != 0) 572 syslog(LOG_WARNING, "can not set signal handler: %m"); 573 574 parse_conf(CONF_PATH "/dma.conf"); 575 576 if (config.authpath != NULL) 577 parse_authfile(config.authpath); 578 579 if (showq) { 580 if (load_queue(&queue) < 0) 581 errlog(1, "can not load queue"); 582 show_queue(&queue); 583 return (0); 584 } 585 586 if (doqueue) { 587 flushqueue_signal(); 588 if (load_queue(&queue) < 0) 589 errlog(1, "can not load queue"); 590 run_queue(&queue); 591 return (0); 592 } 593 594 if (read_aliases() != 0) 595 errlog(1, "could not parse aliases file `%s'", config.aliases); 596 597 if ((sender = set_from(&queue, sender)) == NULL) 598 errlog(1, "set_from failed"); 599 600 if (newspoolf(&queue) != 0) 601 errlog(1, "can not create temp file in `%s'", config.spooldir); 602 603 setlogident("%s", queue.id); 604 605 for (i = 0; i < argc; i++) { 606 if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0) 607 errlogx(1, "invalid recipient `%s'", argv[i]); 608 } 609 610 if (LIST_EMPTY(&queue.queue) && !recp_from_header) 611 errlogx(1, "no recipients"); 612 613 if (readmail(&queue, nodot, recp_from_header) != 0) 614 errlog(1, "can not read mail"); 615 616 if (LIST_EMPTY(&queue.queue)) 617 errlogx(1, "no recipients"); 618 619 if (linkspool(&queue) != 0) 620 errlog(1, "can not create spools"); 621 622 /* From here on the mail is safe. */ 623 624 if (config.features & DEFER || queue_only) 625 return (0); 626 627 run_queue(&queue); 628 629 /* NOTREACHED */ 630 return (0); 631 } 632