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