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 <paths.h> 51 #include <pwd.h> 52 #include <signal.h> 53 #include <stdarg.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <unistd.h> 59 60 #include "dma.h" 61 62 63 static void deliver(struct qitem *); 64 65 struct aliases aliases = LIST_HEAD_INITIALIZER(aliases); 66 struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs); 67 struct authusers authusers = LIST_HEAD_INITIALIZER(authusers); 68 char username[USERNAME_SIZE]; 69 uid_t useruid; 70 const char *logident_base; 71 char errmsg[ERRMSG_SIZE]; 72 73 static int daemonize = 1; 74 static int doqueue = 0; 75 76 struct config config = { 77 .smarthost = NULL, 78 .port = 25, 79 .aliases = "/etc/aliases", 80 .spooldir = "/var/spool/dma", 81 .authpath = NULL, 82 .certfile = NULL, 83 .features = 0, 84 .mailname = NULL, 85 .masquerade_host = NULL, 86 .masquerade_user = NULL, 87 }; 88 89 90 static void 91 sighup_handler(int signo) 92 { 93 (void)signo; /* so that gcc doesn't complain */ 94 } 95 96 static char * 97 set_from(struct queue *queue, const char *osender) 98 { 99 const char *addr; 100 char *sender; 101 102 if (osender) { 103 addr = osender; 104 } else if (getenv("EMAIL") != NULL) { 105 addr = getenv("EMAIL"); 106 } else { 107 if (config.masquerade_user) 108 addr = config.masquerade_user; 109 else 110 addr = username; 111 } 112 113 if (!strchr(addr, '@')) { 114 const char *from_host = hostname(); 115 116 if (config.masquerade_host) 117 from_host = config.masquerade_host; 118 119 if (asprintf(&sender, "%s@%s", addr, from_host) <= 0) 120 return (NULL); 121 } else { 122 sender = strdup(addr); 123 if (sender == NULL) 124 return (NULL); 125 } 126 127 if (strchr(sender, '\n') != NULL) { 128 errno = EINVAL; 129 return (NULL); 130 } 131 132 queue->sender = sender; 133 return (sender); 134 } 135 136 static int 137 read_aliases(void) 138 { 139 yyin = fopen(config.aliases, "r"); 140 if (yyin == NULL) { 141 /* 142 * Non-existing aliases file is not a fatal error 143 */ 144 if (errno == ENOENT) 145 return (0); 146 /* Other problems are. */ 147 return (-1); 148 } 149 if (yyparse()) 150 return (-1); /* fatal error, probably malloc() */ 151 fclose(yyin); 152 return (0); 153 } 154 155 static int 156 do_alias(struct queue *queue, const char *addr) 157 { 158 struct alias *al; 159 struct stritem *sit; 160 int aliased = 0; 161 162 LIST_FOREACH(al, &aliases, next) { 163 if (strcmp(al->alias, addr) != 0) 164 continue; 165 SLIST_FOREACH(sit, &al->dests, next) { 166 if (add_recp(queue, sit->str, EXPAND_ADDR) != 0) 167 return (-1); 168 } 169 aliased = 1; 170 } 171 172 return (aliased); 173 } 174 175 int 176 add_recp(struct queue *queue, const char *str, int expand) 177 { 178 struct qitem *it, *tit; 179 struct passwd *pw; 180 char *host; 181 int aliased = 0; 182 183 it = calloc(1, sizeof(*it)); 184 if (it == NULL) 185 return (-1); 186 it->addr = strdup(str); 187 if (it->addr == NULL) 188 return (-1); 189 190 it->sender = queue->sender; 191 host = strrchr(it->addr, '@'); 192 if (host != NULL && 193 (strcmp(host + 1, hostname()) == 0 || 194 strcmp(host + 1, "localhost") == 0)) { 195 *host = 0; 196 } 197 LIST_FOREACH(tit, &queue->queue, next) { 198 /* weed out duplicate dests */ 199 if (strcmp(tit->addr, it->addr) == 0) { 200 free(it->addr); 201 free(it); 202 return (0); 203 } 204 } 205 LIST_INSERT_HEAD(&queue->queue, it, next); 206 207 /** 208 * Do local delivery if there is no @. 209 * Do not do local delivery when NULLCLIENT is set. 210 */ 211 if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) { 212 it->remote = 0; 213 if (expand) { 214 aliased = do_alias(queue, it->addr); 215 if (!aliased && expand == EXPAND_WILDCARD) 216 aliased = do_alias(queue, "*"); 217 if (aliased < 0) 218 return (-1); 219 if (aliased) { 220 LIST_REMOVE(it, next); 221 } else { 222 /* Local destination, check */ 223 pw = getpwnam(it->addr); 224 if (pw == NULL) 225 goto out; 226 /* XXX read .forward */ 227 endpwent(); 228 } 229 } 230 } else { 231 it->remote = 1; 232 } 233 234 return (0); 235 236 out: 237 free(it->addr); 238 free(it); 239 return (-1); 240 } 241 242 static struct qitem * 243 go_background(struct queue *queue) 244 { 245 struct sigaction sa; 246 struct qitem *it; 247 pid_t pid; 248 249 if (daemonize && daemon(0, 0) != 0) { 250 syslog(LOG_ERR, "can not daemonize: %m"); 251 exit(EX_OSERR); 252 } 253 daemonize = 0; 254 255 bzero(&sa, sizeof(sa)); 256 sa.sa_handler = SIG_IGN; 257 sigaction(SIGCHLD, &sa, NULL); 258 259 LIST_FOREACH(it, &queue->queue, next) { 260 /* No need to fork for the last dest */ 261 if (LIST_NEXT(it, next) == NULL) 262 goto retit; 263 264 pid = fork(); 265 switch (pid) { 266 case -1: 267 syslog(LOG_ERR, "can not fork: %m"); 268 exit(EX_OSERR); 269 break; 270 271 case 0: 272 /* 273 * Child: 274 * 275 * return and deliver mail 276 */ 277 retit: 278 /* 279 * If necessary, acquire the queue and * mail files. 280 * If this fails, we probably were raced by another 281 * process. It is okay to be raced if we're supposed 282 * to flush the queue. 283 */ 284 setlogident("%s", it->queueid); 285 switch (acquirespool(it)) { 286 case 0: 287 break; 288 case 1: 289 if (doqueue) 290 exit(EX_OK); 291 syslog(LOG_WARNING, "could not lock queue file"); 292 exit(EX_SOFTWARE); 293 default: 294 exit(EX_SOFTWARE); 295 } 296 dropspool(queue, it); 297 return (it); 298 299 default: 300 /* 301 * Parent: 302 * 303 * fork next child 304 */ 305 break; 306 } 307 } 308 309 syslog(LOG_CRIT, "reached dead code"); 310 exit(EX_SOFTWARE); 311 } 312 313 static void 314 deliver(struct qitem *it) 315 { 316 int error; 317 unsigned int backoff = MIN_RETRY, slept; 318 struct timeval now; 319 struct stat st; 320 321 snprintf(errmsg, sizeof(errmsg), "unknown bounce reason"); 322 323 retry: 324 syslog(LOG_INFO, "trying delivery"); 325 326 if (it->remote) 327 error = deliver_remote(it); 328 else 329 error = deliver_local(it); 330 331 switch (error) { 332 case 0: 333 delqueue(it); 334 syslog(LOG_INFO, "delivery successful"); 335 exit(EX_OK); 336 337 case 1: 338 if (stat(it->queuefn, &st) != 0) { 339 syslog(LOG_ERR, "lost queue file `%s'", it->queuefn); 340 exit(EX_SOFTWARE); 341 } 342 if (gettimeofday(&now, NULL) == 0 && 343 (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) { 344 snprintf(errmsg, sizeof(errmsg), 345 "Could not deliver for the last %d seconds. Giving up.", 346 MAX_TIMEOUT); 347 goto bounce; 348 } 349 for (slept = 0; slept < backoff;) { 350 slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT); 351 if (flushqueue_since(slept)) { 352 backoff = MIN_RETRY; 353 goto retry; 354 } 355 } 356 if (slept >= backoff) { 357 /* pick the next backoff between [1.5, 2.5) times backoff */ 358 backoff = backoff + backoff / 2 + random() % backoff; 359 if (backoff > MAX_RETRY) 360 backoff = MAX_RETRY; 361 } 362 goto retry; 363 364 case -1: 365 default: 366 break; 367 } 368 369 bounce: 370 bounce(it, errmsg); 371 /* NOTREACHED */ 372 } 373 374 void 375 run_queue(struct queue *queue) 376 { 377 struct qitem *it; 378 379 if (LIST_EMPTY(&queue->queue)) 380 return; 381 382 it = go_background(queue); 383 deliver(it); 384 /* NOTREACHED */ 385 } 386 387 static void 388 show_queue(struct queue *queue) 389 { 390 struct qitem *it; 391 int locked = 0; /* XXX */ 392 393 if (LIST_EMPTY(&queue->queue)) { 394 printf("Mail queue is empty\n"); 395 return; 396 } 397 398 LIST_FOREACH(it, &queue->queue, next) { 399 printf("ID\t: %s%s\n" 400 "From\t: %s\n" 401 "To\t: %s\n", 402 it->queueid, 403 locked ? "*" : "", 404 it->sender, it->addr); 405 406 if (LIST_NEXT(it, next) != NULL) 407 printf("--\n"); 408 } 409 } 410 411 /* 412 * TODO: 413 * 414 * - alias processing 415 * - use group permissions 416 * - proper sysexit codes 417 */ 418 419 int 420 main(int argc, char **argv) 421 { 422 struct sigaction act; 423 char *sender = NULL; 424 struct queue queue; 425 int i, ch; 426 int nodot = 0, showq = 0, queue_only = 0; 427 int recp_from_header = 0; 428 429 set_username(); 430 431 /* 432 * We never run as root. If called by root, drop permissions 433 * to the mail user. 434 */ 435 if (geteuid() == 0 || getuid() == 0) { 436 struct passwd *pw; 437 438 errno = 0; 439 pw = getpwnam(DMA_ROOT_USER); 440 if (pw == NULL) { 441 if (errno == 0) 442 errx(EX_CONFIG, "user '%s' not found", DMA_ROOT_USER); 443 else 444 err(EX_OSERR, "cannot drop root privileges"); 445 } 446 447 if (setuid(pw->pw_uid) != 0) 448 err(EX_OSERR, "cannot drop root privileges"); 449 450 if (geteuid() == 0 || getuid() == 0) 451 errx(EX_OSERR, "cannot drop root privileges"); 452 } 453 454 atexit(deltmp); 455 init_random(); 456 457 bzero(&queue, sizeof(queue)); 458 LIST_INIT(&queue.queue); 459 460 if (strcmp(argv[0], "mailq") == 0) { 461 argv++; argc--; 462 showq = 1; 463 if (argc != 0) 464 errx(EX_USAGE, "invalid arguments"); 465 goto skipopts; 466 } else if (strcmp(argv[0], "newaliases") == 0) { 467 logident_base = "dma"; 468 setlogident("%s", logident_base); 469 470 if (read_aliases() != 0) 471 errx(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases); 472 exit(EX_OK); 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 FALLTRHOUGH */ 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 FALLTRHOUGH */ 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 FALLTRHOUGH */ 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 /* 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 ((sender = set_from(&queue, sender)) == NULL) 599 errlog(EX_SOFTWARE, "set_from()"); 600 601 if (newspoolf(&queue) != 0) 602 errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir); 603 604 setlogident("%s", queue.id); 605 606 for (i = 0; i < argc; i++) { 607 if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0) 608 errlogx(EX_DATAERR, "invalid recipient `%s'", argv[i]); 609 } 610 611 if (LIST_EMPTY(&queue.queue) && !recp_from_header) 612 errlogx(EX_NOINPUT, "no recipients"); 613 614 if (readmail(&queue, nodot, recp_from_header) != 0) 615 errlog(EX_NOINPUT, "can not read mail"); 616 617 if (LIST_EMPTY(&queue.queue)) 618 errlogx(EX_NOINPUT, "no recipients"); 619 620 if (linkspool(&queue) != 0) 621 errlog(EX_CANTCREAT, "can not create spools"); 622 623 /* From here on the mail is safe. */ 624 625 if (config.features & DEFER || queue_only) 626 return (0); 627 628 run_queue(&queue); 629 630 /* NOTREACHED */ 631 return (0); 632 } 633