X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=dma.c;h=b87b20245583314f67b038f56db3dc1ae6c35f5a;hb=af455b4c7286295ffd1a80b8a768ee06231c48f3;hp=e6192a15095327d449dd9d4b0a5eac70f6cfceba;hpb=2266bfc063bd9039360af3b7a7a0c136f7411077;p=people%2Fms%2Fdma.git diff --git a/dma.c b/dma.c index e6192a1..b87b202 100644 --- a/dma.c +++ b/dma.c @@ -1,8 +1,9 @@ /* + * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>. * Copyright (c) 2008 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project - * by Simon 'corecode' Schubert . + * by Simon Schubert <2@0x2c.org>. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -64,21 +65,25 @@ static void deliver(struct qitem *); struct aliases aliases = LIST_HEAD_INITIALIZER(aliases); struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs); struct authusers authusers = LIST_HEAD_INITIALIZER(authusers); -const char *username; +char username[USERNAME_SIZE]; +uid_t useruid; const char *logident_base; +char errmsg[ERRMSG_SIZE]; static int daemonize = 1; +static int doqueue = 0; struct config config = { .smarthost = NULL, .port = 25, - .aliases = "/var/mail/aliases", + .aliases = "/etc/aliases", .spooldir = "/var/spool/dma", .authpath = NULL, .certfile = NULL, .features = 0, .mailname = NULL, - .mailnamefile = NULL, + .masquerade_host = NULL, + .masquerade_user = NULL, }; @@ -91,18 +96,31 @@ sighup_handler(int signo) static char * set_from(struct queue *queue, const char *osender) { + const char *addr; char *sender; if (osender) { - sender = strdup(osender); - if (sender == NULL) - return (NULL); + addr = osender; } else if (getenv("EMAIL") != NULL) { - sender = strdup(getenv("EMAIL")); - if (sender == NULL) + addr = getenv("EMAIL"); + } else { + if (config.masquerade_user) + addr = config.masquerade_user; + else + addr = username; + } + + if (!strchr(addr, '@')) { + const char *from_host = hostname(); + + if (config.masquerade_host) + from_host = config.masquerade_host; + + if (asprintf(&sender, "%s@%s", addr, from_host) <= 0) return (NULL); } else { - if (asprintf(&sender, "%s@%s", username, hostname()) <= 0) + sender = strdup(addr); + if (sender == NULL) return (NULL); } @@ -134,12 +152,30 @@ read_aliases(void) return (0); } +static int +do_alias(struct queue *queue, const char *addr) +{ + struct alias *al; + struct stritem *sit; + int aliased = 0; + + LIST_FOREACH(al, &aliases, next) { + if (strcmp(al->alias, addr) != 0) + continue; + SLIST_FOREACH(sit, &al->dests, next) { + if (add_recp(queue, sit->str, EXPAND_ADDR) != 0) + return (-1); + } + aliased = 1; + } + + return (aliased); +} + int add_recp(struct queue *queue, const char *str, int expand) { struct qitem *it, *tit; - struct stritem *sit; - struct alias *al; struct passwd *pw; char *host; int aliased = 0; @@ -167,18 +203,19 @@ add_recp(struct queue *queue, const char *str, int expand) } } LIST_INSERT_HEAD(&queue->queue, it, next); - if (strrchr(it->addr, '@') == NULL) { + + /** + * Do local delivery if there is no @. + * Do not do local delivery when NULLCLIENT is set. + */ + if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) { it->remote = 0; if (expand) { - LIST_FOREACH(al, &aliases, next) { - if (strcmp(al->alias, it->addr) != 0) - continue; - SLIST_FOREACH(sit, &al->dests, next) { - if (add_recp(queue, sit->str, 1) != 0) - return (-1); - } - aliased = 1; - } + aliased = do_alias(queue, it->addr); + if (!aliased && expand == EXPAND_WILDCARD) + aliased = do_alias(queue, "*"); + if (aliased < 0) + return (-1); if (aliased) { LIST_REMOVE(it, next); } else { @@ -211,12 +248,11 @@ go_background(struct queue *queue) if (daemonize && daemon(0, 0) != 0) { syslog(LOG_ERR, "can not daemonize: %m"); - exit(1); + exit(EX_OSERR); } daemonize = 0; bzero(&sa, sizeof(sa)); - sa.sa_flags = SA_NOCLDWAIT; sa.sa_handler = SIG_IGN; sigaction(SIGCHLD, &sa, NULL); @@ -229,7 +265,7 @@ go_background(struct queue *queue) switch (pid) { case -1: syslog(LOG_ERR, "can not fork: %m"); - exit(1); + exit(EX_OSERR); break; case 0: @@ -242,11 +278,21 @@ retit: /* * If necessary, acquire the queue and * mail files. * If this fails, we probably were raced by another - * process. + * process. It is okay to be raced if we're supposed + * to flush the queue. */ setlogident("%s", it->queueid); - if (acquirespool(it) < 0) - exit(1); + switch (acquirespool(it)) { + case 0: + break; + case 1: + if (doqueue) + exit(EX_OK); + syslog(LOG_WARNING, "could not lock queue file"); + exit(EX_SOFTWARE); + default: + exit(EX_SOFTWARE); + } dropspool(queue, it); return (it); @@ -261,48 +307,58 @@ retit: } syslog(LOG_CRIT, "reached dead code"); - exit(1); + exit(EX_SOFTWARE); } static void deliver(struct qitem *it) { int error; - unsigned int backoff = MIN_RETRY; - const char *errmsg = "unknown bounce reason"; + unsigned int backoff = MIN_RETRY, slept; struct timeval now; struct stat st; + snprintf(errmsg, sizeof(errmsg), "unknown bounce reason"); + retry: - syslog(LOG_INFO, "trying delivery"); + syslog(LOG_INFO, "<%s> trying delivery", it->addr); if (it->remote) - error = deliver_remote(it, &errmsg); + error = deliver_remote(it); else - error = deliver_local(it, &errmsg); + error = deliver_local(it); switch (error) { case 0: delqueue(it); - syslog(LOG_INFO, "delivery successful"); - exit(0); + syslog(LOG_INFO, "<%s> delivery successful", it->addr); + exit(EX_OK); case 1: if (stat(it->queuefn, &st) != 0) { syslog(LOG_ERR, "lost queue file `%s'", it->queuefn); - exit(1); + exit(EX_SOFTWARE); } if (gettimeofday(&now, NULL) == 0 && (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) { - asprintf(__DECONST(void *, &errmsg), + snprintf(errmsg, sizeof(errmsg), "Could not deliver for the last %d seconds. Giving up.", MAX_TIMEOUT); goto bounce; } - if (sleep(backoff) == 0) - backoff *= 2; - if (backoff > MAX_RETRY) - backoff = MAX_RETRY; + for (slept = 0; slept < backoff;) { + slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT); + if (flushqueue_since(slept)) { + backoff = MIN_RETRY; + goto retry; + } + } + if (slept >= backoff) { + /* pick the next backoff between [1.5, 2.5) times backoff */ + backoff = backoff + backoff / 2 + random() % backoff; + if (backoff > MAX_RETRY) + backoff = MAX_RETRY; + } goto retry; case -1: @@ -367,9 +423,34 @@ main(int argc, char **argv) char *sender = NULL; struct queue queue; int i, ch; - int nodot = 0, doqueue = 0, showq = 0, queue_only = 0; + int nodot = 0, showq = 0, queue_only = 0; int recp_from_header = 0; + set_username(); + + /* + * We never run as root. If called by root, drop permissions + * to the mail user. + */ + if (geteuid() == 0 || getuid() == 0) { + struct passwd *pw; + + errno = 0; + pw = getpwnam(DMA_ROOT_USER); + if (pw == NULL) { + if (errno == 0) + errx(EX_CONFIG, "user '%s' not found", DMA_ROOT_USER); + else + err(EX_OSERR, "cannot drop root privileges"); + } + + if (setuid(pw->pw_uid) != 0) + err(EX_OSERR, "cannot drop root privileges"); + + if (geteuid() == 0 || getuid() == 0) + errx(EX_OSERR, "cannot drop root privileges"); + } + atexit(deltmp); init_random(); @@ -380,8 +461,15 @@ main(int argc, char **argv) argv++; argc--; showq = 1; if (argc != 0) - errx(1, "invalid arguments"); + errx(EX_USAGE, "invalid arguments"); goto skipopts; + } else if (strcmp(argv[0], "newaliases") == 0) { + logident_base = "dma"; + setlogident(NULL); + + if (read_aliases() != 0) + errx(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases); + exit(EX_OK); } opterr = 0; @@ -430,6 +518,9 @@ main(int argc, char **argv) break; case 'q': + /* Don't let getopt slup up other arguments */ + if (optarg && *optarg == '-') + optind--; doqueue = 1; break; @@ -457,7 +548,7 @@ main(int argc, char **argv) default: fprintf(stderr, "invalid argument: `-%c'\n", optopt); - exit(1); + exit(EX_USAGE); } } argc -= optind; @@ -465,18 +556,15 @@ main(int argc, char **argv) opterr = 1; if (argc != 0 && (showq || doqueue)) - errx(1, "sending mail and queue operations are mutually exclusive"); + errx(EX_USAGE, "sending mail and queue operations are mutually exclusive"); if (showq + doqueue > 1) - errx(1, "conflicting queue operations"); + errx(EX_USAGE, "conflicting queue operations"); skipopts: if (logident_base == NULL) logident_base = "dma"; setlogident(NULL); - set_username(); - - /* XXX fork root here */ act.sa_handler = sighup_handler; act.sa_flags = 0; @@ -484,52 +572,53 @@ skipopts: if (sigaction(SIGHUP, &act, NULL) != 0) syslog(LOG_WARNING, "can not set signal handler: %m"); - parse_conf(CONF_PATH); + parse_conf(CONF_PATH "/dma.conf"); if (config.authpath != NULL) parse_authfile(config.authpath); if (showq) { if (load_queue(&queue) < 0) - errlog(1, "can not load queue"); + errlog(EX_NOINPUT, "can not load queue"); show_queue(&queue); return (0); } if (doqueue) { + flushqueue_signal(); if (load_queue(&queue) < 0) - errlog(1, "can not load queue"); + errlog(EX_NOINPUT, "can not load queue"); run_queue(&queue); return (0); } if (read_aliases() != 0) - errlog(1, "can not read aliases file `%s'", config.aliases); + errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases); if ((sender = set_from(&queue, sender)) == NULL) - errlog(1, NULL); + errlog(EX_SOFTWARE, NULL); if (newspoolf(&queue) != 0) - errlog(1, "can not create temp file"); + errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir); setlogident("%s", queue.id); for (i = 0; i < argc; i++) { - if (add_recp(&queue, argv[i], 1) != 0) - errlogx(1, "invalid recipient `%s'", argv[i]); + if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0) + errlogx(EX_DATAERR, "invalid recipient `%s'", argv[i]); } if (LIST_EMPTY(&queue.queue) && !recp_from_header) - errlogx(1, "no recipients"); + errlogx(EX_NOINPUT, "no recipients"); if (readmail(&queue, nodot, recp_from_header) != 0) - errlog(1, "can not read mail"); + errlog(EX_NOINPUT, "can not read mail"); if (LIST_EMPTY(&queue.queue)) - errlogx(1, "no recipients"); + errlogx(EX_NOINPUT, "no recipients"); if (linkspool(&queue) != 0) - errlog(1, "can not create spools"); + errlog(EX_CANTCREAT, "can not create spools"); /* From here on the mail is safe. */