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 74 struct config config = { 75 .smarthost = NULL, 76 .port = 25, 77 .aliases = "/etc/aliases", 78 .spooldir = "/var/spool/dma", 79 .authpath = NULL, 80 .certfile = NULL, 81 .features = 0, 82 .mailname = NULL, 83 .masquerade_host = NULL, 84 .masquerade_user = NULL, 85 }; 86 87 88 static void 89 sighup_handler(int signo) 90 { 91 (void)signo; /* so that gcc doesn't complain */ 92 } 93 94 static char * 95 set_from(struct queue *queue, const char *osender) 96 { 97 char *sender; 98 99 if (osender) { 100 sender = strdup(osender); 101 if (sender == NULL) 102 return (NULL); 103 } else if (getenv("EMAIL") != NULL) { 104 sender = strdup(getenv("EMAIL")); 105 if (sender == NULL) 106 return (NULL); 107 } else { 108 const char *from_user = username; 109 const char *from_host = hostname(); 110 111 if (config.masquerade_user) 112 from_user = config.masquerade_user; 113 if (config.masquerade_host) 114 from_host = config.masquerade_host; 115 if (asprintf(&sender, "%s@%s", from_user, from_host) <= 0) 116 return (NULL); 117 } 118 119 if (strchr(sender, '\n') != NULL) { 120 errno = EINVAL; 121 return (NULL); 122 } 123 124 queue->sender = sender; 125 return (sender); 126 } 127 128 static int 129 read_aliases(void) 130 { 131 yyin = fopen(config.aliases, "r"); 132 if (yyin == NULL) { 133 /* 134 * Non-existing aliases file is not a fatal error 135 */ 136 if (errno == ENOENT) 137 return (0); 138 /* Other problems are. */ 139 return (-1); 140 } 141 if (yyparse()) 142 return (-1); /* fatal error, probably malloc() */ 143 fclose(yyin); 144 return (0); 145 } 146 147 static int 148 do_alias(struct queue *queue, const char *addr) 149 { 150 struct alias *al; 151 struct stritem *sit; 152 int aliased = 0; 153 154 LIST_FOREACH(al, &aliases, next) { 155 if (strcmp(al->alias, addr) != 0) 156 continue; 157 SLIST_FOREACH(sit, &al->dests, next) { 158 if (add_recp(queue, sit->str, EXPAND_ADDR) != 0) 159 return (-1); 160 } 161 aliased = 1; 162 } 163 164 return (aliased); 165 } 166 167 int 168 add_recp(struct queue *queue, const char *str, int expand) 169 { 170 struct qitem *it, *tit; 171 struct passwd *pw; 172 char *host; 173 int aliased = 0; 174 175 it = calloc(1, sizeof(*it)); 176 if (it == NULL) 177 return (-1); 178 it->addr = strdup(str); 179 if (it->addr == NULL) 180 return (-1); 181 182 it->sender = queue->sender; 183 host = strrchr(it->addr, '@'); 184 if (host != NULL && 185 (strcmp(host + 1, hostname()) == 0 || 186 strcmp(host + 1, "localhost") == 0)) { 187 *host = 0; 188 } 189 LIST_FOREACH(tit, &queue->queue, next) { 190 /* weed out duplicate dests */ 191 if (strcmp(tit->addr, it->addr) == 0) { 192 free(it->addr); 193 free(it); 194 return (0); 195 } 196 } 197 LIST_INSERT_HEAD(&queue->queue, it, next); 198 if (strrchr(it->addr, '@') == NULL) { 199 it->remote = 0; 200 if (expand) { 201 aliased = do_alias(queue, it->addr); 202 if (!aliased && expand == EXPAND_WILDCARD) 203 aliased = do_alias(queue, "*"); 204 if (aliased < 0) 205 return (-1); 206 if (aliased) { 207 LIST_REMOVE(it, next); 208 } else { 209 /* Local destination, check */ 210 pw = getpwnam(it->addr); 211 if (pw == NULL) 212 goto out; 213 /* XXX read .forward */ 214 endpwent(); 215 } 216 } 217 } else { 218 it->remote = 1; 219 } 220 221 return (0); 222 223 out: 224 free(it->addr); 225 free(it); 226 return (-1); 227 } 228 229 static struct qitem * 230 go_background(struct queue *queue) 231 { 232 struct sigaction sa; 233 struct qitem *it; 234 pid_t pid; 235 236 if (daemonize && daemon(0, 0) != 0) { 237 syslog(LOG_ERR, "can not daemonize: %m"); 238 exit(1); 239 } 240 daemonize = 0; 241 242 bzero(&sa, sizeof(sa)); 243 sa.sa_handler = SIG_IGN; 244 sigaction(SIGCHLD, &sa, NULL); 245 246 LIST_FOREACH(it, &queue->queue, next) { 247 /* No need to fork for the last dest */ 248 if (LIST_NEXT(it, next) == NULL) 249 goto retit; 250 251 pid = fork(); 252 switch (pid) { 253 case -1: 254 syslog(LOG_ERR, "can not fork: %m"); 255 exit(1); 256 break; 257 258 case 0: 259 /* 260 * Child: 261 * 262 * return and deliver mail 263 */ 264 retit: 265 /* 266 * If necessary, acquire the queue and * mail files. 267 * If this fails, we probably were raced by another 268 * process. 269 */ 270 setlogident("%s", it->queueid); 271 if (acquirespool(it) < 0) 272 exit(1); 273 dropspool(queue, it); 274 return (it); 275 276 default: 277 /* 278 * Parent: 279 * 280 * fork next child 281 */ 282 break; 283 } 284 } 285 286 syslog(LOG_CRIT, "reached dead code"); 287 exit(1); 288 } 289 290 static void 291 deliver(struct qitem *it) 292 { 293 int error; 294 unsigned int backoff = MIN_RETRY; 295 struct timeval now; 296 struct stat st; 297 298 snprintf(errmsg, sizeof(errmsg), "unknown bounce reason"); 299 300 retry: 301 syslog(LOG_INFO, "trying delivery"); 302 303 if (it->remote) 304 error = deliver_remote(it); 305 else 306 error = deliver_local(it); 307 308 switch (error) { 309 case 0: 310 delqueue(it); 311 syslog(LOG_INFO, "delivery successful"); 312 exit(0); 313 314 case 1: 315 if (stat(it->queuefn, &st) != 0) { 316 syslog(LOG_ERR, "lost queue file `%s'", it->queuefn); 317 exit(1); 318 } 319 if (gettimeofday(&now, NULL) == 0 && 320 (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) { 321 snprintf(errmsg, sizeof(errmsg), 322 "Could not deliver for the last %d seconds. Giving up.", 323 MAX_TIMEOUT); 324 goto bounce; 325 } 326 if (sleep(backoff) == 0) { 327 /* pick the next backoff between [1.5, 2.5) times backoff */ 328 backoff = backoff + backoff / 2 + random() % backoff; 329 if (backoff > MAX_RETRY) 330 backoff = MAX_RETRY; 331 } 332 goto retry; 333 334 case -1: 335 default: 336 break; 337 } 338 339 bounce: 340 bounce(it, errmsg); 341 /* NOTREACHED */ 342 } 343 344 void 345 run_queue(struct queue *queue) 346 { 347 struct qitem *it; 348 349 if (LIST_EMPTY(&queue->queue)) 350 return; 351 352 it = go_background(queue); 353 deliver(it); 354 /* NOTREACHED */ 355 } 356 357 static void 358 show_queue(struct queue *queue) 359 { 360 struct qitem *it; 361 int locked = 0; /* XXX */ 362 363 if (LIST_EMPTY(&queue->queue)) { 364 printf("Mail queue is empty\n"); 365 return; 366 } 367 368 LIST_FOREACH(it, &queue->queue, next) { 369 printf("ID\t: %s%s\n" 370 "From\t: %s\n" 371 "To\t: %s\n", 372 it->queueid, 373 locked ? "*" : "", 374 it->sender, it->addr); 375 376 if (LIST_NEXT(it, next) != NULL) 377 printf("--\n"); 378 } 379 } 380 381 /* 382 * TODO: 383 * 384 * - alias processing 385 * - use group permissions 386 * - proper sysexit codes 387 */ 388 389 int 390 main(int argc, char **argv) 391 { 392 struct sigaction act; 393 char *sender = NULL; 394 struct queue queue; 395 int i, ch; 396 int nodot = 0, doqueue = 0, showq = 0, queue_only = 0; 397 int recp_from_header = 0; 398 399 set_username(); 400 401 /* 402 * We never run as root. If called by root, drop permissions 403 * to the mail user. 404 */ 405 if (geteuid() == 0 || getuid() == 0) { 406 struct passwd *pw; 407 408 pw = getpwnam(DMA_ROOT_USER); 409 if (pw == NULL) 410 err(1, "cannot drop root privileges"); 411 412 if (setuid(pw->pw_uid) != 0) 413 err(1, "cannot drop root privileges"); 414 415 if (geteuid() == 0 || getuid() == 0) 416 errx(1, "cannot drop root privileges"); 417 } 418 419 atexit(deltmp); 420 init_random(); 421 422 bzero(&queue, sizeof(queue)); 423 LIST_INIT(&queue.queue); 424 425 if (strcmp(argv[0], "mailq") == 0) { 426 argv++; argc--; 427 showq = 1; 428 if (argc != 0) 429 errx(1, "invalid arguments"); 430 goto skipopts; 431 } 432 433 opterr = 0; 434 while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) { 435 switch (ch) { 436 case 'A': 437 /* -AX is being ignored, except for -A{c,m} */ 438 if (optarg[0] == 'c' || optarg[0] == 'm') { 439 break; 440 } 441 /* else FALLTRHOUGH */ 442 case 'b': 443 /* -bX is being ignored, except for -bp */ 444 if (optarg[0] == 'p') { 445 showq = 1; 446 break; 447 } else if (optarg[0] == 'q') { 448 queue_only = 1; 449 break; 450 } 451 /* else FALLTRHOUGH */ 452 case 'D': 453 daemonize = 0; 454 break; 455 case 'L': 456 logident_base = optarg; 457 break; 458 case 'f': 459 case 'r': 460 sender = optarg; 461 break; 462 463 case 't': 464 recp_from_header = 1; 465 break; 466 467 case 'o': 468 /* -oX is being ignored, except for -oi */ 469 if (optarg[0] != 'i') 470 break; 471 /* else FALLTRHOUGH */ 472 case 'O': 473 break; 474 case 'i': 475 nodot = 1; 476 break; 477 478 case 'q': 479 doqueue = 1; 480 break; 481 482 /* Ignored options */ 483 case 'B': 484 case 'C': 485 case 'd': 486 case 'F': 487 case 'h': 488 case 'N': 489 case 'n': 490 case 'R': 491 case 'U': 492 case 'V': 493 case 'v': 494 case 'X': 495 break; 496 497 case ':': 498 if (optopt == 'q') { 499 doqueue = 1; 500 break; 501 } 502 /* FALLTHROUGH */ 503 504 default: 505 fprintf(stderr, "invalid argument: `-%c'\n", optopt); 506 exit(1); 507 } 508 } 509 argc -= optind; 510 argv += optind; 511 opterr = 1; 512 513 if (argc != 0 && (showq || doqueue)) 514 errx(1, "sending mail and queue operations are mutually exclusive"); 515 516 if (showq + doqueue > 1) 517 errx(1, "conflicting queue operations"); 518 519 skipopts: 520 if (logident_base == NULL) 521 logident_base = "dma"; 522 setlogident(NULL); 523 524 act.sa_handler = sighup_handler; 525 act.sa_flags = 0; 526 sigemptyset(&act.sa_mask); 527 if (sigaction(SIGHUP, &act, NULL) != 0) 528 syslog(LOG_WARNING, "can not set signal handler: %m"); 529 530 parse_conf(CONF_PATH "/dma.conf"); 531 532 if (config.authpath != NULL) 533 parse_authfile(config.authpath); 534 535 if (showq) { 536 if (load_queue(&queue) < 0) 537 errlog(1, "can not load queue"); 538 show_queue(&queue); 539 return (0); 540 } 541 542 if (doqueue) { 543 if (load_queue(&queue) < 0) 544 errlog(1, "can not load queue"); 545 run_queue(&queue); 546 return (0); 547 } 548 549 if (read_aliases() != 0) 550 errlog(1, "can not read aliases file `%s'", config.aliases); 551 552 if ((sender = set_from(&queue, sender)) == NULL) 553 errlog(1, NULL); 554 555 if (newspoolf(&queue) != 0) 556 errlog(1, "can not create temp file"); 557 558 setlogident("%s", queue.id); 559 560 for (i = 0; i < argc; i++) { 561 if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0) 562 errlogx(1, "invalid recipient `%s'", argv[i]); 563 } 564 565 if (LIST_EMPTY(&queue.queue) && !recp_from_header) 566 errlogx(1, "no recipients"); 567 568 if (readmail(&queue, nodot, recp_from_header) != 0) 569 errlog(1, "can not read mail"); 570 571 if (LIST_EMPTY(&queue.queue)) 572 errlogx(1, "no recipients"); 573 574 if (linkspool(&queue) != 0) 575 errlog(1, "can not create spools"); 576 577 /* From here on the mail is safe. */ 578 579 if (config.features & DEFER || queue_only) 580 return (0); 581 582 run_queue(&queue); 583 584 /* NOTREACHED */ 585 return (0); 586 } 587