diff options
author | Gilles Chehade <gilles@poolp.org> | 2020-05-22 14:32:22 +0200 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2020-05-22 14:32:22 +0200 |
commit | 90620a574d8824e5b2aa18709f2d5b5b6bb3cb38 (patch) | |
tree | 251ab90cb9264a2cd476a19b33e066868a4f7849 | |
parent | mv back (diff) | |
download | OpenSMTPD-90620a574d8824e5b2aa18709f2d5b5b6bb3cb38.tar.xz OpenSMTPD-90620a574d8824e5b2aa18709f2d5b5b6bb3cb38.zip |
moving smtpd to usr.sbin/smtpd to ease cherry-picking of upstream
120 files changed, 6 insertions, 54419 deletions
diff --git a/contrib/libexec/encrypt/Makefile.am b/contrib/libexec/encrypt/Makefile.am index 6ad7b82d..2f96e60d 100644 --- a/contrib/libexec/encrypt/Makefile.am +++ b/contrib/libexec/encrypt/Makefile.am @@ -1,7 +1,7 @@ pkglibexec_PROGRAMS = encrypt encrypt_SOURCES = encrypt.c -encrypt_SOURCES += $(top_srcdir)/smtpd/log.c +encrypt_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat diff --git a/contrib/libexec/lockspool/Makefile.am b/contrib/libexec/lockspool/Makefile.am index dacf5386..2801c101 100644 --- a/contrib/libexec/lockspool/Makefile.am +++ b/contrib/libexec/lockspool/Makefile.am @@ -2,7 +2,7 @@ pkglibexec_PROGRAMS = lockspool lockspool_SOURCES = lockspool.c lockspool_SOURCES += locking.c -lockspool_SOURCES += $(top_srcdir)/smtpd/log.c +lockspool_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c EXTRA_DIST = mail.local.h pathnames.h diff --git a/contrib/libexec/mail.local/Makefile.am b/contrib/libexec/mail.local/Makefile.am index bd5211a2..217659c1 100644 --- a/contrib/libexec/mail.local/Makefile.am +++ b/contrib/libexec/mail.local/Makefile.am @@ -2,7 +2,7 @@ pkglibexec_PROGRAMS = mail.local mail_local_SOURCES = mail.local.c mail_local_SOURCES += locking.c -mail_local_SOURCES += $(top_srcdir)/smtpd/log.c +mail_local_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c EXTRA_DIST = mail.local.h pathnames.h diff --git a/mk/pathnames b/mk/pathnames index b233ec33..efa785ef 100644 --- a/mk/pathnames +++ b/mk/pathnames @@ -1,8 +1,8 @@ -smtpd_srcdir = $(top_srcdir)/smtpd +smtpd_srcdir = $(top_srcdir)/usr.sbin/smtpd compat_srcdir = $(top_srcdir)/openbsd-compat regress_srcdir = $(top_srcdir)/regress/bin -PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \ +PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \ -DPATH_CHROOT=\"$(PRIVSEP_PATH)\" \ -DPATH_SMTPCTL=\"$(sbindir)/smtpctl\" \ -DPATH_MAILLOCAL=\"$(pkglibexecdir)/mail.local\" \ diff --git a/openbsd-compat/Makefile.am b/openbsd-compat/Makefile.am index 01a3efe7..0e704cfd 100644 --- a/openbsd-compat/Makefile.am +++ b/openbsd-compat/Makefile.am @@ -1,6 +1,6 @@ noinst_LIBRARIES = libopenbsd.a -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/smtpd -I$(top_srcdir)/openbsd-compat +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/usr.sbin/smtpd -I$(top_srcdir)/openbsd-compat libopenbsd_a_SOURCES = empty.c diff --git a/smtpd/Makefile b/smtpd/Makefile deleted file mode 100644 index a3dbc9d1..00000000 --- a/smtpd/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# $OpenBSD: Makefile,v 1.18 2018/05/24 11:38:24 gilles Exp $ - -.include <bsd.own.mk> - -SUBDIR = smtpd -SUBDIR+= smtpctl -SUBDIR+= smtp -SUBDIR+= mail - -.include <bsd.subdir.mk> diff --git a/smtpd/aliases.5 b/smtpd/aliases.5 deleted file mode 100644 index 7c250c81..00000000 --- a/smtpd/aliases.5 +++ /dev/null @@ -1,102 +0,0 @@ -.\" $OpenBSD: aliases.5,v 1.16 2020/04/23 21:28:10 jmc Exp $ -.\" -.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: April 23 2020 $ -.Dt ALIASES 5 -.Os -.Sh NAME -.Nm aliases -.Nd aliases file for smtpd -.Sh DESCRIPTION -This manual page describes the format of the -.Nm -file, as used by -.Xr smtpd 8 . -An alias in its simplest form is used to assign an arbitrary name -to an email address, or a group of email addresses. -This provides a convenient way to send mail. -For example an alias could refer to all users of a group: -email to that alias would be sent to all members of the group. -Much more complex aliases can be defined however: -an alias can refer to other aliases, -be used to send mail to a file instead of another person, -or to execute various commands. -.Pp -Within the file, -.Ql # -is a comment delimiter; anything placed after it is discarded. -The file consists of key/value mappings of the form: -.Bd -filled -offset indent -key: value1, value2, value3, ... -.Ed -.Pp -.Em key -is always folded to lowercase before alias lookups to ensure that -there can be no ambiguity. -The key is expanded to the corresponding values, -which consist of one or more of the following: -.Bl -tag -width Ds -.It Em user -A user on the host machine. -The user must have a valid entry in the -.Xr passwd 5 -database file. -.It Ar /path/to/file -Append messages to -.Ar file , -specified by its absolute pathname. -.It | Ns Ar command -Pipe the message to -.Ar command -on its standard input. -The command is run under the privileges of the daemon's unprivileged account. -.It : Ns Ar include : Ns Ar /path/to/file -Include any definitions in -.Ar file -as alias entries. -The format of the file is identical to this one. -.It Ar user-part@domain-part -An email address in RFC 5322 format. -If an address extension is appended to the user-part, -it is first compared for an exact match. -It is then stripped so that an address such as user+ext@example.com -will only use the part that precedes -.Sq + -as a -.Em key . -.It Ar error : Ns Ar code message -A status code and message to return. -The code must be 3 digits, -starting 4XX (TempFail) or 5XX (PermFail). -The message must be present and can be freely chosen. -.El -.Sh FILES -.Bl -tag -width "/etc/mail/aliasesXXX" -compact -.It Pa /etc/mail/aliases -Default -.Nm -file. -.El -.Sh SEE ALSO -.Xr smtpd.conf 5 , -.Xr makemap 8 , -.Xr newaliases 8 , -.Xr smtpd 8 -.Sh HISTORY -The -.Nm -file format appeared in -.Bx 4.0 . diff --git a/smtpd/aliases.c b/smtpd/aliases.c deleted file mode 100644 index 0f8a5c1e..00000000 --- a/smtpd/aliases.c +++ /dev/null @@ -1,234 +0,0 @@ -/* $OpenBSD: aliases.c,v 1.78 2020/04/28 21:46:43 eric Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#ifdef HAVE_LIBUTIL_H -#include <libutil.h> -#endif - -#include "smtpd.h" -#include "log.h" - -static int aliases_expand_include(struct expand *, const char *); - -int -aliases_get(struct expand *expand, const char *username) -{ - struct expandnode *xn; - char buf[SMTPD_MAXLOCALPARTSIZE]; - size_t nbaliases; - int ret; - union lookup lk; - struct dispatcher *dsp; - struct table *mapping = NULL; - char *pbuf; - - dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher); - mapping = table_find(env, dsp->u.local.table_alias); - - xlowercase(buf, username, sizeof(buf)); - - /* first, check if entry has a user-part tag */ - pbuf = strchr(buf, *env->sc_subaddressing_delim); - if (pbuf) { - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret < 0) - return (-1); - if (ret) - goto expand; - *pbuf = '\0'; - } - - /* no user-part tag, try looking up user */ - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret <= 0) - return ret; - -expand: - /* foreach node in table_alias expandtree, we merge */ - nbaliases = 0; - RB_FOREACH(xn, expandtree, &lk.expand->tree) { - if (xn->type == EXPAND_INCLUDE) - nbaliases += aliases_expand_include(expand, - xn->u.buffer); - else { - expand_insert(expand, xn); - nbaliases++; - } - } - - expand_free(lk.expand); - - log_debug("debug: aliases_get: returned %zd aliases", nbaliases); - return nbaliases; -} - -int -aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr) -{ - struct expandnode *xn; - union lookup lk; - char buf[LINE_MAX]; - char user[LINE_MAX]; - char tag[LINE_MAX]; - char domain[LINE_MAX]; - char *pbuf; - int nbaliases; - int ret; - struct dispatcher *dsp; - struct table *mapping = NULL; - - dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher); - mapping = table_find(env, dsp->u.local.table_virtual); - - if (!bsnprintf(user, sizeof(user), "%s", maddr->user)) - return 0; - if (!bsnprintf(domain, sizeof(domain), "%s", maddr->domain)) - return 0; - xlowercase(user, user, sizeof(user)); - xlowercase(domain, domain, sizeof(domain)); - - memset(tag, '\0', sizeof tag); - pbuf = strchr(user, *env->sc_subaddressing_delim); - if (pbuf) { - if (!bsnprintf(tag, sizeof(tag), "%s", pbuf + 1)) - return 0; - xlowercase(tag, tag, sizeof(tag)); - *pbuf = '\0'; - } - - /* first, check if entry has a user-part tag */ - if (tag[0]) { - if (!bsnprintf(buf, sizeof(buf), "%s%c%s@%s", - user, *env->sc_subaddressing_delim, tag, domain)) - return 0; - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret < 0) - return (-1); - if (ret) - goto expand; - } - - /* then, check if entry exists without user-part tag */ - if (!bsnprintf(buf, sizeof(buf), "%s@%s", user, domain)) - return 0; - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret < 0) - return (-1); - if (ret) - goto expand; - - if (tag[0]) { - /* Failed ? We lookup for username + user-part tag */ - if (!bsnprintf(buf, sizeof(buf), "%s%c%s", - user, *env->sc_subaddressing_delim, tag)) - return 0; - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret < 0) - return (-1); - if (ret) - goto expand; - } - - /* Failed ? We lookup for username only */ - if (!bsnprintf(buf, sizeof(buf), "%s", user)) - return 0; - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret < 0) - return (-1); - if (ret) - goto expand; - - /* Do not try catch-all entries if there is no domain */ - if (domain[0] == '\0') - return 0; - - if (!bsnprintf(buf, sizeof(buf), "@%s", domain)) - return 0; - /* Failed ? We lookup for catch all for virtual domain */ - ret = table_lookup(mapping, K_ALIAS, buf, &lk); - if (ret < 0) - return (-1); - if (ret) - goto expand; - - /* Failed ? We lookup for a *global* catch all */ - ret = table_lookup(mapping, K_ALIAS, "@", &lk); - if (ret <= 0) - return (ret); - -expand: - /* foreach node in table_virtual expand, we merge */ - nbaliases = 0; - RB_FOREACH(xn, expandtree, &lk.expand->tree) { - if (xn->type == EXPAND_INCLUDE) - nbaliases += aliases_expand_include(expand, - xn->u.buffer); - else { - expand_insert(expand, xn); - nbaliases++; - } - } - - expand_free(lk.expand); - - log_debug("debug: aliases_virtual_get: '%s' resolved to %d nodes", - buf, nbaliases); - - return nbaliases; -} - -static int -aliases_expand_include(struct expand *expand, const char *filename) -{ - FILE *fp; - char *line; - size_t len, lineno = 0; - char delim[3] = { '\\', '#', '\0' }; - - fp = fopen(filename, "r"); - if (fp == NULL) { - log_warn("warn: failed to open include file \"%s\".", filename); - return 0; - } - - while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) { - expand_line(expand, line, 0); - free(line); - } - - fclose(fp); - return 1; -} diff --git a/smtpd/bounce.c b/smtpd/bounce.c deleted file mode 100644 index 4a4a0992..00000000 --- a/smtpd/bounce.c +++ /dev/null @@ -1,820 +0,0 @@ -/* $OpenBSD: bounce.c,v 1.82 2020/04/24 11:34:07 eric Exp $ */ - -/* - * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define BOUNCE_MAXRUN 2 -#define BOUNCE_HIWAT 65535 - -enum { - BOUNCE_EHLO, - BOUNCE_MAIL, - BOUNCE_RCPT, - BOUNCE_DATA, - BOUNCE_DATA_NOTICE, - BOUNCE_DATA_MESSAGE, - BOUNCE_DATA_END, - BOUNCE_QUIT, - BOUNCE_CLOSE, -}; - -struct bounce_envelope { - TAILQ_ENTRY(bounce_envelope) entry; - uint64_t id; - struct mailaddr dest; - char *report; - uint8_t esc_class; - uint8_t esc_code; -}; - -struct bounce_message { - SPLAY_ENTRY(bounce_message) sp_entry; - TAILQ_ENTRY(bounce_message) entry; - uint32_t msgid; - struct delivery_bounce bounce; - char *smtpname; - char *to; - time_t timeout; - TAILQ_HEAD(, bounce_envelope) envelopes; -}; - -struct bounce_session { - char *smtpname; - struct bounce_message *msg; - FILE *msgfp; - int state; - struct io *io; - uint64_t boundary; -}; - -SPLAY_HEAD(bounce_message_tree, bounce_message); -static int bounce_message_cmp(const struct bounce_message *, - const struct bounce_message *); -SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry, - bounce_message_cmp); - -static void bounce_drain(void); -static void bounce_send(struct bounce_session *, const char *, ...); -static int bounce_next_message(struct bounce_session *); -static int bounce_next(struct bounce_session *); -static void bounce_delivery(struct bounce_message *, int, const char *); -static void bounce_status(struct bounce_session *, const char *, ...); -static void bounce_io(struct io *, int, void *); -static void bounce_timeout(int, short, void *); -static void bounce_free(struct bounce_session *); -static const char *action_str(const struct delivery_bounce *); - -static struct tree wait_fd; -static struct bounce_message_tree messages; -static TAILQ_HEAD(, bounce_message) pending; - -static int nmessage = 0; -static int running = 0; -static struct event ev_timer; - -static void -bounce_init(void) -{ - static int init = 0; - - if (init == 0) { - TAILQ_INIT(&pending); - SPLAY_INIT(&messages); - tree_init(&wait_fd); - evtimer_set(&ev_timer, bounce_timeout, NULL); - init = 1; - } -} - -void -bounce_add(uint64_t evpid) -{ - char buf[LINE_MAX], *line; - struct envelope evp; - struct bounce_message key, *msg; - struct bounce_envelope *be; - - bounce_init(); - - if (queue_envelope_load(evpid, &evp) == 0) { - m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_close(p_scheduler); - return; - } - - if (evp.type != D_BOUNCE) - errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!", - evp.id); - - key.msgid = evpid_to_msgid(evpid); - key.bounce = evp.agent.bounce; - key.smtpname = evp.smtpname; - - switch (evp.esc_class) { - case ESC_STATUS_OK: - key.bounce.type = B_DELIVERED; - break; - case ESC_STATUS_TEMPFAIL: - key.bounce.type = B_DELAYED; - break; - default: - key.bounce.type = B_FAILED; - } - - key.bounce.dsn_ret = evp.dsn_ret; - key.bounce.ttl = evp.ttl; - msg = SPLAY_FIND(bounce_message_tree, &messages, &key); - if (msg == NULL) { - msg = xcalloc(1, sizeof(*msg)); - msg->msgid = key.msgid; - msg->bounce = key.bounce; - - TAILQ_INIT(&msg->envelopes); - - msg->smtpname = xstrdup(evp.smtpname); - (void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user, - evp.sender.domain); - msg->to = xstrdup(buf); - nmessage += 1; - SPLAY_INSERT(bounce_message_tree, &messages, msg); - log_debug("debug: bounce: new message %08" PRIx32, - msg->msgid); - stat_increment("bounce.message", 1); - } else - TAILQ_REMOVE(&pending, msg, entry); - - line = evp.errorline; - if (strlen(line) > 4 && (*line == '1' || *line == '6')) - line += 4; - (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user, - evp.dest.domain, line); - - be = xmalloc(sizeof *be); - be->id = evpid; - be->report = xstrdup(buf); - (void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user)); - (void)strlcpy(be->dest.domain, evp.dest.domain, - sizeof(be->dest.domain)); - be->esc_class = evp.esc_class; - be->esc_code = evp.esc_code; - TAILQ_INSERT_TAIL(&msg->envelopes, be, entry); - log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report); - - msg->timeout = time(NULL) + 1; - TAILQ_INSERT_TAIL(&pending, msg, entry); - - stat_increment("bounce.envelope", 1); - bounce_drain(); -} - -void -bounce_fd(int fd) -{ - struct bounce_session *s; - struct bounce_message *msg; - - log_debug("debug: bounce: got enqueue socket %d", fd); - - if (fd == -1 || TAILQ_EMPTY(&pending)) { - log_debug("debug: bounce: cancelling"); - if (fd != -1) - close(fd); - running -= 1; - bounce_drain(); - return; - } - - msg = TAILQ_FIRST(&pending); - - s = xcalloc(1, sizeof(*s)); - s->smtpname = xstrdup(msg->smtpname); - s->state = BOUNCE_EHLO; - s->io = io_new(); - io_set_callback(s->io, bounce_io, s); - io_set_fd(s->io, fd); - io_set_timeout(s->io, 30000); - io_set_read(s->io); - s->boundary = generate_uid(); - - log_debug("debug: bounce: new session %p", s); - stat_increment("bounce.session", 1); -} - -static void -bounce_timeout(int fd, short ev, void *arg) -{ - log_debug("debug: bounce: timeout"); - - bounce_drain(); -} - -static void -bounce_drain() -{ - struct bounce_message *msg; - struct timeval tv; - time_t t; - - log_debug("debug: bounce: drain: nmessage=%d running=%d", - nmessage, running); - - while (1) { - if (running >= BOUNCE_MAXRUN) { - log_debug("debug: bounce: max session reached"); - return; - } - - if (nmessage == 0) { - log_debug("debug: bounce: no more messages"); - return; - } - - if (running >= nmessage) { - log_debug("debug: bounce: enough sessions running"); - return; - } - - if ((msg = TAILQ_FIRST(&pending)) == NULL) { - log_debug("debug: bounce: no more pending messages"); - return; - } - - t = time(NULL); - if (msg->timeout > t) { - log_debug("debug: bounce: next message not ready yet"); - if (!evtimer_pending(&ev_timer, NULL)) { - log_debug("debug: bounce: setting timer"); - tv.tv_sec = msg->timeout - t; - tv.tv_usec = 0; - evtimer_add(&ev_timer, &tv); - } - return; - } - - log_debug("debug: bounce: requesting new enqueue socket..."); - m_compose(p_pony, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0); - - running += 1; - } -} - -static void -bounce_send(struct bounce_session *s, const char *fmt, ...) -{ - va_list ap; - char *p; - int len; - - va_start(ap, fmt); - if ((len = vasprintf(&p, fmt, ap)) == -1) - fatal("bounce: vasprintf"); - va_end(ap); - - log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p); - - io_xprintf(s->io, "%s\r\n", p); - - free(p); -} - -static const char * -bounce_duration(long long int d) -{ - static char buf[32]; - - if (d < 60) { - (void)snprintf(buf, sizeof buf, "%lld second%s", d, - (d == 1) ? "" : "s"); - } else if (d < 3600) { - d = d / 60; - (void)snprintf(buf, sizeof buf, "%lld minute%s", d, - (d == 1) ? "" : "s"); - } - else if (d < 3600 * 24) { - d = d / 3600; - (void)snprintf(buf, sizeof buf, "%lld hour%s", d, - (d == 1) ? "" : "s"); - } - else { - d = d / (3600 * 24); - (void)snprintf(buf, sizeof buf, "%lld day%s", d, - (d == 1) ? "" : "s"); - } - return (buf); -} - -#define NOTICE_INTRO \ - " Hi!\r\n\r\n" \ - " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n" - -const char *notice_error = - " An error has occurred while attempting to deliver a message for\r\n" - " the following list of recipients:\r\n\r\n"; - -const char *notice_warning = - " A message is delayed for more than %s for the following\r\n" - " list of recipients:\r\n\r\n"; - -const char *notice_warning2 = - " Please note that this is only a temporary failure report.\r\n" - " The message is kept in the queue for up to %s.\r\n" - " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n"; - -const char *notice_success = - " Your message was successfully delivered to these recipients.\r\n\r\n"; - -const char *notice_relay = - " Your message was relayed to these recipients.\r\n\r\n"; - -static int -bounce_next_message(struct bounce_session *s) -{ - struct bounce_message *msg; - char buf[LINE_MAX]; - int fd; - time_t now; - - again: - - now = time(NULL); - - TAILQ_FOREACH(msg, &pending, entry) { - if (msg->timeout > now) - continue; - if (strcmp(msg->smtpname, s->smtpname)) - continue; - break; - } - if (msg == NULL) - return (0); - - TAILQ_REMOVE(&pending, msg, entry); - SPLAY_REMOVE(bounce_message_tree, &messages, msg); - - if ((fd = queue_message_fd_r(msg->msgid)) == -1) { - bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, - "Could not open message fd"); - goto again; - } - - if ((s->msgfp = fdopen(fd, "r")) == NULL) { - (void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno)); - log_warn("warn: bounce: fdopen"); - close(fd); - bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf); - goto again; - } - - s->msg = msg; - return (1); -} - -static int -bounce_next(struct bounce_session *s) -{ - struct bounce_envelope *evp; - char *line = NULL; - size_t n, sz = 0; - ssize_t len; - - switch (s->state) { - case BOUNCE_EHLO: - bounce_send(s, "EHLO %s", s->smtpname); - s->state = BOUNCE_MAIL; - break; - - case BOUNCE_MAIL: - case BOUNCE_DATA_END: - log_debug("debug: bounce: %p: getting next message...", s); - if (bounce_next_message(s) == 0) { - log_debug("debug: bounce: %p: no more messages", s); - bounce_send(s, "QUIT"); - s->state = BOUNCE_CLOSE; - break; - } - log_debug("debug: bounce: %p: found message %08"PRIx32, - s, s->msg->msgid); - bounce_send(s, "MAIL FROM: <>"); - s->state = BOUNCE_RCPT; - break; - - case BOUNCE_RCPT: - bounce_send(s, "RCPT TO: <%s>", s->msg->to); - s->state = BOUNCE_DATA; - break; - - case BOUNCE_DATA: - bounce_send(s, "DATA"); - s->state = BOUNCE_DATA_NOTICE; - break; - - case BOUNCE_DATA_NOTICE: - /* Construct an appropriate notice. */ - - io_xprintf(s->io, - "Subject: Delivery status notification: %s\r\n" - "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n" - "To: %s\r\n" - "Date: %s\r\n" - "MIME-Version: 1.0\r\n" - "Content-Type: multipart/mixed;" - "boundary=\"%16" PRIu64 "/%s\"\r\n" - "\r\n" - "This is a MIME-encapsulated message.\r\n" - "\r\n", - action_str(&s->msg->bounce), - s->smtpname, - s->msg->to, - time_to_text(time(NULL)), - s->boundary, - s->smtpname); - - io_xprintf(s->io, - "--%16" PRIu64 "/%s\r\n" - "Content-Description: Notification\r\n" - "Content-Type: text/plain; charset=us-ascii\r\n" - "\r\n" - NOTICE_INTRO - "\r\n", - s->boundary, s->smtpname); - - switch (s->msg->bounce.type) { - case B_FAILED: - io_xprint(s->io, notice_error); - break; - case B_DELAYED: - io_xprintf(s->io, notice_warning, - bounce_duration(s->msg->bounce.delay)); - break; - case B_DELIVERED: - io_xprint(s->io, s->msg->bounce.mta_without_dsn ? - notice_relay : notice_success); - break; - default: - log_warn("warn: bounce: unknown bounce_type"); - } - - TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { - io_xprint(s->io, evp->report); - io_xprint(s->io, "\r\n"); - } - io_xprint(s->io, "\r\n"); - - if (s->msg->bounce.type == B_DELAYED) - io_xprintf(s->io, notice_warning2, - bounce_duration(s->msg->bounce.ttl)); - - io_xprintf(s->io, - " Below is a copy of the original message:\r\n" - "\r\n"); - - io_xprintf(s->io, - "--%16" PRIu64 "/%s\r\n" - "Content-Description: Delivery Report\r\n" - "Content-Type: message/delivery-status\r\n" - "\r\n", - s->boundary, s->smtpname); - - io_xprintf(s->io, - "Reporting-MTA: dns; %s\r\n" - "\r\n", - s->smtpname); - - TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { - io_xprintf(s->io, - "Final-Recipient: rfc822; %s@%s\r\n" - "Action: %s\r\n" - "Status: %s\r\n" - "\r\n", - evp->dest.user, - evp->dest.domain, - action_str(&s->msg->bounce), - esc_code(evp->esc_class, evp->esc_code)); - } - - log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", - s, io_queued(s->io)); - - s->state = BOUNCE_DATA_MESSAGE; - break; - - case BOUNCE_DATA_MESSAGE: - io_xprintf(s->io, - "--%16" PRIu64 "/%s\r\n" - "Content-Description: Message headers\r\n" - "Content-Type: text/rfc822-headers\r\n" - "\r\n", - s->boundary, s->smtpname); - - n = io_queued(s->io); - while (io_queued(s->io) < BOUNCE_HIWAT) { - if ((len = getline(&line, &sz, s->msgfp)) == -1) - break; - if (len == 1 && line[0] == '\n' && /* end of headers */ - s->msg->bounce.type == B_DELIVERED && - s->msg->bounce.dsn_ret == DSN_RETHDRS) { - free(line); - fclose(s->msgfp); - s->msgfp = NULL; - io_xprintf(s->io, - "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, - s->smtpname); - bounce_send(s, "."); - s->state = BOUNCE_DATA_END; - return (0); - } - line[len - 1] = '\0'; - io_xprintf(s->io, "%s%s\r\n", - (len == 2 && line[0] == '.') ? "." : "", line); - } - free(line); - - if (ferror(s->msgfp)) { - fclose(s->msgfp); - s->msgfp = NULL; - bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, - "Error reading message"); - s->msg = NULL; - return (-1); - } - - io_xprintf(s->io, - "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname); - - log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", - s, io_queued(s->io) - n); - - if (feof(s->msgfp)) { - fclose(s->msgfp); - s->msgfp = NULL; - bounce_send(s, "."); - s->state = BOUNCE_DATA_END; - } - break; - - case BOUNCE_QUIT: - bounce_send(s, "QUIT"); - s->state = BOUNCE_CLOSE; - break; - - default: - fatalx("bounce: bad state"); - } - - return (0); -} - - -static void -bounce_delivery(struct bounce_message *msg, int delivery, const char *status) -{ - struct bounce_envelope *be; - struct envelope evp; - size_t n; - const char *f; - - n = 0; - while ((be = TAILQ_FIRST(&msg->envelopes))) { - if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) { - if (queue_envelope_load(be->id, &evp) == 0) { - fatalx("could not reload envelope!"); - } - evp.retry++; - evp.lasttry = msg->timeout; - envelope_set_errormsg(&evp, "%s", status); - queue_envelope_update(&evp); - m_create(p_scheduler, delivery, 0, 0, -1); - m_add_envelope(p_scheduler, &evp); - m_close(p_scheduler); - } else { - m_create(p_scheduler, delivery, 0, 0, -1); - m_add_evpid(p_scheduler, be->id); - m_close(p_scheduler); - queue_envelope_delete(be->id); - } - TAILQ_REMOVE(&msg->envelopes, be, entry); - free(be->report); - free(be); - n += 1; - } - - - if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) - f = "TempFail"; - else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL) - f = "PermFail"; - else - f = NULL; - - if (f) - log_warnx("warn: %s injecting failure report on message %08" - PRIx32 " to <%s> for %zu envelope%s: %s", - f, msg->msgid, msg->to, n, n > 1 ? "s":"", status); - - nmessage -= 1; - stat_decrement("bounce.message", 1); - stat_decrement("bounce.envelope", n); - free(msg->smtpname); - free(msg->to); - free(msg); -} - -static void -bounce_status(struct bounce_session *s, const char *fmt, ...) -{ - va_list ap; - char *status; - int len, delivery; - - /* Ignore if there is no message */ - if (s->msg == NULL) - return; - - va_start(ap, fmt); - if ((len = vasprintf(&status, fmt, ap)) == -1) - fatal("bounce: vasprintf"); - va_end(ap); - - if (*status == '2') - delivery = IMSG_QUEUE_DELIVERY_OK; - else if (*status == '5' || *status == '6') - delivery = IMSG_QUEUE_DELIVERY_PERMFAIL; - else - delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL; - - bounce_delivery(s->msg, delivery, status); - s->msg = NULL; - if (s->msgfp) - fclose(s->msgfp); - - free(status); -} - -static void -bounce_free(struct bounce_session *s) -{ - log_debug("debug: bounce: %p: deleting session", s); - - io_free(s->io); - - free(s->smtpname); - free(s); - - running -= 1; - stat_decrement("bounce.session", 1); - bounce_drain(); -} - -static void -bounce_io(struct io *io, int evt, void *arg) -{ - struct bounce_session *s = arg; - const char *error; - char *line, *msg; - int cont; - size_t len; - - log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt), - io_strio(io)); - - switch (evt) { - case IO_DATAIN: - nextline: - line = io_getline(s->io, &len); - if (line == NULL && io_datalen(s->io) >= LINE_MAX) { - bounce_status(s, "Input too long"); - bounce_free(s); - return; - } - - if (line == NULL) - break; - - /* Strip trailing '\r' */ - if (len && line[len - 1] == '\r') - line[--len] = '\0'; - - log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line); - - if ((error = parse_smtp_response(line, len, &msg, &cont))) { - bounce_status(s, "Bad response: %s", error); - bounce_free(s); - return; - } - if (cont) - goto nextline; - - if (s->state == BOUNCE_CLOSE) { - bounce_free(s); - return; - } - - if (line[0] != '2' && line[0] != '3') { /* fail */ - bounce_status(s, "%s", line); - s->state = BOUNCE_QUIT; - } else if (s->state == BOUNCE_DATA_END) { /* accepted */ - bounce_status(s, "%s", line); - } - - if (bounce_next(s) == -1) { - bounce_free(s); - return; - } - - io_set_write(io); - break; - - case IO_LOWAT: - if (s->state == BOUNCE_DATA_MESSAGE) - if (bounce_next(s) == -1) { - bounce_free(s); - return; - } - if (io_queued(s->io) == 0) - io_set_read(io); - break; - - default: - bounce_status(s, "442 i/o error %d", evt); - bounce_free(s); - break; - } -} - -static int -bounce_message_cmp(const struct bounce_message *a, - const struct bounce_message *b) -{ - int r; - - if (a->msgid < b->msgid) - return (-1); - if (a->msgid > b->msgid) - return (1); - if ((r = strcmp(a->smtpname, b->smtpname))) - return (r); - - return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce)); -} - -static const char * -action_str(const struct delivery_bounce *b) -{ - switch (b->type) { - case B_FAILED: - return ("failed"); - case B_DELAYED: - return ("delayed"); - case B_DELIVERED: - if (b->mta_without_dsn) - return ("relayed"); - - return ("delivered"); - default: - log_warn("warn: bounce: unknown bounce_type"); - return (""); - } -} - -SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry, - bounce_message_cmp); diff --git a/smtpd/ca.c b/smtpd/ca.c deleted file mode 100644 index a27db87a..00000000 --- a/smtpd/ca.c +++ /dev/null @@ -1,777 +0,0 @@ -/* $OpenBSD: ca.c,v 1.36 2019/09/21 07:46:53 semarie Exp $ */ - -/* - * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/socket.h> -#include <sys/tree.h> - -#include <grp.h> /* needed for setgroups */ -#include <err.h> -#include <imsg.h> -#include <limits.h> -#include <pwd.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <openssl/ssl.h> -#include <openssl/pem.h> -#include <openssl/evp.h> -#include <openssl/ecdsa.h> -#include <openssl/rsa.h> -#include <openssl/engine.h> -#include <openssl/err.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -static int ca_verify_cb(int, X509_STORE_CTX *); - -static int rsae_send_imsg(int, const unsigned char *, unsigned char *, - RSA *, int, unsigned int); -static int rsae_pub_enc(int, const unsigned char *, unsigned char *, - RSA *, int); -static int rsae_pub_dec(int,const unsigned char *, unsigned char *, - RSA *, int); -static int rsae_priv_enc(int, const unsigned char *, unsigned char *, - RSA *, int); -static int rsae_priv_dec(int, const unsigned char *, unsigned char *, - RSA *, int); -static int rsae_mod_exp(BIGNUM *, const BIGNUM *, RSA *, BN_CTX *); -static int rsae_bn_mod_exp(BIGNUM *, const BIGNUM *, const BIGNUM *, - const BIGNUM *, BN_CTX *, BN_MONT_CTX *); -static int rsae_init(RSA *); -static int rsae_finish(RSA *); -static int rsae_keygen(RSA *, int, BIGNUM *, BN_GENCB *); - -#if defined(SUPPORT_ECDSA) -static ECDSA_SIG *ecdsae_do_sign(const unsigned char *, int, const BIGNUM *, - const BIGNUM *, EC_KEY *); -static int ecdsae_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **); -static int ecdsae_do_verify(const unsigned char *, int, const ECDSA_SIG *, - EC_KEY *); -#endif - -static uint64_t reqid = 0; - -static void -ca_shutdown(void) -{ - log_debug("debug: ca agent exiting"); - _exit(0); -} - -int -ca(void) -{ - struct passwd *pw; - - purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES|PURGE_DISPATCHERS); - - if ((pw = getpwnam(SMTPD_USER)) == NULL) - fatalx("unknown user " SMTPD_USER); - - if (chroot(PATH_CHROOT) == -1) - fatal("ca: chroot"); - if (chdir("/") == -1) - fatal("ca: chdir(\"/\")"); - - config_process(PROC_CA); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("ca: cannot drop privileges"); - - imsg_callback = ca_imsg; - event_init(); - - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - - config_peer(PROC_CONTROL); - config_peer(PROC_PARENT); - config_peer(PROC_PONY); - - /* Ignore them until we get our config */ - mproc_disable(p_pony); - -#if HAVE_PLEDGE - if (pledge("stdio", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} - -void -ca_init(void) -{ - BIO *in = NULL; - EVP_PKEY *pkey = NULL; - struct pki *pki; - const char *k; - void *iter_dict; - - log_debug("debug: init private ssl-tree"); - iter_dict = NULL; - while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { - if (pki->pki_key == NULL) - continue; - - if ((in = BIO_new_mem_buf(pki->pki_key, - pki->pki_key_len)) == NULL) - fatalx("ca_launch: key"); - - if ((pkey = PEM_read_bio_PrivateKey(in, - NULL, NULL, NULL)) == NULL) - fatalx("ca_launch: PEM"); - BIO_free(in); - - pki->pki_pkey = pkey; - - freezero(pki->pki_key, pki->pki_key_len); - pki->pki_key = NULL; - } -} - -static int -ca_verify_cb(int ok, X509_STORE_CTX *ctx) -{ - switch (X509_STORE_CTX_get_error(ctx)) { - case X509_V_OK: - break; - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - break; - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: - break; - case X509_V_ERR_CERT_HAS_EXPIRED: - case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: - break; - case X509_V_ERR_NO_EXPLICIT_POLICY: - break; - } - return ok; -} - -int -ca_X509_verify(void *certificate, void *chain, const char *CAfile, - const char *CRLfile, const char **errstr) -{ - X509_STORE *store = NULL; - X509_STORE_CTX *xsc = NULL; - int ret = 0; - long error = 0; - - if ((store = X509_STORE_new()) == NULL) - goto end; - - if (!X509_STORE_load_locations(store, CAfile, NULL)) { - log_warn("warn: unable to load CA file %s", CAfile); - goto end; - } - X509_STORE_set_default_paths(store); - - if ((xsc = X509_STORE_CTX_new()) == NULL) - goto end; - - if (X509_STORE_CTX_init(xsc, store, certificate, chain) != 1) - goto end; - - X509_STORE_CTX_set_verify_cb(xsc, ca_verify_cb); - - ret = X509_verify_cert(xsc); - -end: - *errstr = NULL; - if (ret != 1) { - if (xsc) { - error = X509_STORE_CTX_get_error(xsc); - *errstr = X509_verify_cert_error_string(error); - } - else if (ERR_peek_last_error()) - *errstr = ERR_error_string(ERR_peek_last_error(), NULL); - } - - X509_STORE_CTX_free(xsc); - X509_STORE_free(store); - - return ret > 0 ? 1 : 0; -} - -void -ca_imsg(struct mproc *p, struct imsg *imsg) -{ - RSA *rsa = NULL; -#if defined(SUPPORT_ECDSA) - EC_KEY *ecdsa = NULL; -#endif - const void *from = NULL; - unsigned char *to = NULL; - struct msg m; - const char *pkiname; - size_t flen, tlen, padding; -#if defined(SUPPORT_ECDSA) - int buf_len; -#endif - struct pki *pki; - int ret = 0; - uint64_t id; - int v; - - if (imsg == NULL) - ca_shutdown(); - - switch (imsg->hdr.type) { - case IMSG_CONF_START: - return; - case IMSG_CONF_END: - ca_init(); - - /* Start fulfilling requests */ - mproc_enable(p_pony); - return; - - case IMSG_CTL_VERBOSE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - log_trace_verbose(v); - return; - - case IMSG_CTL_PROFILE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - profiling = v; - return; - - case IMSG_CA_RSA_PRIVENC: - case IMSG_CA_RSA_PRIVDEC: - m_msg(&m, imsg); - m_get_id(&m, &id); - m_get_string(&m, &pkiname); - m_get_data(&m, &from, &flen); - m_get_size(&m, &tlen); - m_get_size(&m, &padding); - m_end(&m); - - pki = dict_get(env->sc_pki_dict, pkiname); - if (pki == NULL || pki->pki_pkey == NULL || - (rsa = EVP_PKEY_get1_RSA(pki->pki_pkey)) == NULL) - fatalx("ca_imsg: invalid pki"); - - if ((to = calloc(1, tlen)) == NULL) - fatalx("ca_imsg: calloc"); - - switch (imsg->hdr.type) { - case IMSG_CA_RSA_PRIVENC: - ret = RSA_private_encrypt(flen, from, to, rsa, - padding); - break; - case IMSG_CA_RSA_PRIVDEC: - ret = RSA_private_decrypt(flen, from, to, rsa, - padding); - break; - } - - m_create(p, imsg->hdr.type, 0, 0, -1); - m_add_id(p, id); - m_add_int(p, ret); - if (ret > 0) - m_add_data(p, to, (size_t)ret); - m_close(p); - - free(to); - RSA_free(rsa); - return; - -#if defined(SUPPORT_ECDSA) - case IMSG_CA_ECDSA_SIGN: - m_msg(&m, imsg); - m_get_id(&m, &id); - m_get_string(&m, &pkiname); - m_get_data(&m, &from, &flen); - m_end(&m); - - pki = dict_get(env->sc_pki_dict, pkiname); - if (pki == NULL || pki->pki_pkey == NULL || - (ecdsa = EVP_PKEY_get1_EC_KEY(pki->pki_pkey)) == NULL) - fatalx("ca_imsg: invalid pki"); - - buf_len = ECDSA_size(ecdsa); - if ((to = calloc(1, buf_len)) == NULL) - fatalx("ca_imsg: calloc"); - ret = ECDSA_sign(0, from, flen, to, &buf_len, ecdsa); - m_create(p, imsg->hdr.type, 0, 0, -1); - m_add_id(p, id); - m_add_int(p, ret); - if (ret > 0) - m_add_data(p, to, (size_t)buf_len); - m_close(p); - free(to); - EC_KEY_free(ecdsa); - return; -#endif - } - errx(1, "ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -/* - * RSA privsep engine (called from unprivileged processes) - */ - -const RSA_METHOD *rsa_default = NULL; - -static RSA_METHOD *rsae_method = NULL; - -static int -rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to, - RSA *rsa, int padding, unsigned int cmd) -{ - int ret = 0; - struct imsgbuf *ibuf; - struct imsg imsg; - int n, done = 0; - const void *toptr; - char *pkiname; - size_t tlen; - struct msg m; - uint64_t id; - - if ((pkiname = RSA_get_ex_data(rsa, 0)) == NULL) - return (0); - - /* - * Send a synchronous imsg because we cannot defer the RSA - * operation in OpenSSL's engine layer. - */ - m_create(p_ca, cmd, 0, 0, -1); - reqid++; - m_add_id(p_ca, reqid); - m_add_string(p_ca, pkiname); - m_add_data(p_ca, (const void *)from, (size_t)flen); - m_add_size(p_ca, (size_t)RSA_size(rsa)); - m_add_size(p_ca, (size_t)padding); - m_flush(p_ca); - - ibuf = &p_ca->imsgbuf; - - while (!done) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatalx("imsg_read"); - if (n == 0) - fatalx("pipe closed"); - - while (!done) { - if ((n = imsg_get(ibuf, &imsg)) == -1) - fatalx("imsg_get error"); - if (n == 0) - break; - - log_imsg(PROC_PONY, PROC_CA, &imsg); - - switch (imsg.hdr.type) { - case IMSG_CA_RSA_PRIVENC: - case IMSG_CA_RSA_PRIVDEC: - break; - default: - /* Another imsg is queued up in the buffer */ - pony_imsg(p_ca, &imsg); - imsg_free(&imsg); - continue; - } - - m_msg(&m, &imsg); - m_get_id(&m, &id); - if (id != reqid) - fatalx("invalid response id"); - m_get_int(&m, &ret); - if (ret > 0) - m_get_data(&m, &toptr, &tlen); - m_end(&m); - - if (ret > 0) - memcpy(to, toptr, tlen); - done = 1; - - imsg_free(&imsg); - } - } - mproc_event_add(p_ca); - - return (ret); -} - -static int -rsae_pub_enc(int flen,const unsigned char *from, unsigned char *to, RSA *rsa, - int padding) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (RSA_meth_get_pub_enc(rsa_default)(flen, from, to, rsa, padding)); -} - -static int -rsae_pub_dec(int flen,const unsigned char *from, unsigned char *to, RSA *rsa, - int padding) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (RSA_meth_get_pub_dec(rsa_default)(flen, from, to, rsa, padding)); -} - -static int -rsae_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, - int padding) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - if (RSA_get_ex_data(rsa, 0) != NULL) - return (rsae_send_imsg(flen, from, to, rsa, padding, - IMSG_CA_RSA_PRIVENC)); - return (RSA_meth_get_priv_enc(rsa_default)(flen, from, to, rsa, padding)); -} - -static int -rsae_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, - int padding) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - if (RSA_get_ex_data(rsa, 0) != NULL) - return (rsae_send_imsg(flen, from, to, rsa, padding, - IMSG_CA_RSA_PRIVDEC)); - - return (RSA_meth_get_priv_dec(rsa_default)(flen, from, to, rsa, padding)); -} - -static int -rsae_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (RSA_meth_get_mod_exp(rsa_default)(r0, I, rsa, ctx)); -} - -static int -rsae_bn_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, - const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (RSA_meth_get_bn_mod_exp(rsa_default)(r, a, p, m, ctx, m_ctx)); -} - -static int -rsae_init(RSA *rsa) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - if (RSA_meth_get_init(rsa_default) == NULL) - return (1); - return (RSA_meth_get_init(rsa_default)(rsa)); -} - -static int -rsae_finish(RSA *rsa) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - if (RSA_meth_get_finish(rsa_default) == NULL) - return (1); - return (RSA_meth_get_finish(rsa_default)(rsa)); -} - -static int -rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (RSA_meth_get_keygen(rsa_default)(rsa, bits, e, cb)); -} - - -#if defined(SUPPORT_ECDSA) -/* - * ECDSA privsep engine (called from unprivileged processes) - */ - -const ECDSA_METHOD *ecdsa_default = NULL; - -static ECDSA_METHOD *ecdsae_method = NULL; - -ECDSA_METHOD * -ECDSA_METHOD_new_temporary(const char *name, int); - -ECDSA_METHOD * -ECDSA_METHOD_new_temporary(const char *name, int flags) -{ - ECDSA_METHOD *ecdsa; - - if ((ecdsa = calloc(1, sizeof (*ecdsa))) == NULL) - return NULL; - - if ((ecdsa->name = strdup(name)) == NULL) { - free(ecdsa); - return NULL; - } - - ecdsa->flags = flags; - return ecdsa; -} - -static ECDSA_SIG * -ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len, - const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) -{ - int ret = 0; - struct imsgbuf *ibuf; - struct imsg imsg; - int n, done = 0; - const void *toptr; - char *pkiname; - size_t tlen; - struct msg m; - uint64_t id; - ECDSA_SIG *sig = NULL; - - if ((pkiname = ECDSA_get_ex_data(eckey, 0)) == NULL) - return (0); - - /* - * Send a synchronous imsg because we cannot defer the ECDSA - * operation in OpenSSL's engine layer. - */ - m_create(p_ca, IMSG_CA_ECDSA_SIGN, 0, 0, -1); - reqid++; - m_add_id(p_ca, reqid); - m_add_string(p_ca, pkiname); - m_add_data(p_ca, (const void *)dgst, (size_t)dgst_len); - m_flush(p_ca); - - ibuf = &p_ca->imsgbuf; - - while (!done) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - fatalx("imsg_read"); - if (n == 0) - fatalx("pipe closed"); - while (!done) { - if ((n = imsg_get(ibuf, &imsg)) == -1) - fatalx("imsg_get error"); - if (n == 0) - break; - - log_imsg(PROC_PONY, PROC_CA, &imsg); - - switch (imsg.hdr.type) { - case IMSG_CA_ECDSA_SIGN: - break; - default: - /* Another imsg is queued up in the buffer */ - pony_imsg(p_ca, &imsg); - imsg_free(&imsg); - continue; - } - - m_msg(&m, &imsg); - m_get_id(&m, &id); - if (id != reqid) - fatalx("invalid response id"); - m_get_int(&m, &ret); - if (ret > 0) - m_get_data(&m, &toptr, &tlen); - m_end(&m); - done = 1; - - if (ret > 0) - d2i_ECDSA_SIG(&sig, (const unsigned char **)&toptr, tlen); - imsg_free(&imsg); - } - } - mproc_event_add(p_ca); - - return (sig); -} - -ECDSA_SIG * -ecdsae_do_sign(const unsigned char *dgst, int dgst_len, - const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - if (ECDSA_get_ex_data(eckey, 0) != NULL) - return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey)); - return (ecdsa_default->ecdsa_do_sign(dgst, dgst_len, inv, rp, eckey)); -} - -int -ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, - BIGNUM **r) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (ecdsa_default->ecdsa_sign_setup(eckey, ctx, kinv, r)); -} - -int -ecdsae_do_verify(const unsigned char *dgst, int dgst_len, - const ECDSA_SIG *sig, EC_KEY *eckey) -{ - log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); - return (ecdsa_default->ecdsa_do_verify(dgst, dgst_len, sig, eckey)); -} -#endif - -static void -rsa_engine_init(void) -{ - ENGINE *e; - const char *errstr, *name; - - if ((rsae_method = RSA_meth_new("RSA privsep engine", 0)) == NULL) { - errstr = "RSA_meth_new"; - goto fail; - } - - RSA_meth_set_pub_enc(rsae_method, rsae_pub_enc); - RSA_meth_set_pub_dec(rsae_method, rsae_pub_dec); - RSA_meth_set_priv_enc(rsae_method, rsae_priv_enc); - RSA_meth_set_priv_dec(rsae_method, rsae_priv_dec); - RSA_meth_set_mod_exp(rsae_method, rsae_mod_exp); - RSA_meth_set_bn_mod_exp(rsae_method, rsae_bn_mod_exp); - RSA_meth_set_init(rsae_method, rsae_init); - RSA_meth_set_finish(rsae_method, rsae_finish); - RSA_meth_set_keygen(rsae_method, rsae_keygen); - - if ((e = ENGINE_get_default_RSA()) == NULL) { - if ((e = ENGINE_new()) == NULL) { - errstr = "ENGINE_new"; - goto fail; - } - if (!ENGINE_set_name(e, RSA_meth_get0_name(rsae_method))) { - errstr = "ENGINE_set_name"; - goto fail; - } - if ((rsa_default = RSA_get_default_method()) == NULL) { - errstr = "RSA_get_default_method"; - goto fail; - } - } else if ((rsa_default = ENGINE_get_RSA(e)) == NULL) { - errstr = "ENGINE_get_RSA"; - goto fail; - } - - if ((name = ENGINE_get_name(e)) == NULL) - name = "unknown RSA engine"; - - log_debug("debug: %s: using %s", __func__, name); - - if (RSA_meth_get_mod_exp(rsa_default) == NULL) - RSA_meth_set_mod_exp(rsae_method, NULL); - if (RSA_meth_get_bn_mod_exp(rsa_default) == NULL) - RSA_meth_set_bn_mod_exp(rsae_method, NULL); - if (RSA_meth_get_keygen(rsa_default) == NULL) - RSA_meth_set_keygen(rsae_method, NULL); - RSA_meth_set_flags(rsae_method, - RSA_meth_get_flags(rsa_default) | RSA_METHOD_FLAG_NO_CHECK); - RSA_meth_set0_app_data(rsae_method, - RSA_meth_get0_app_data(rsa_default)); - - if (!ENGINE_set_RSA(e, rsae_method)) { - errstr = "ENGINE_set_RSA"; - goto fail; - } - if (!ENGINE_set_default_RSA(e)) { - errstr = "ENGINE_set_default_RSA"; - goto fail; - } - - return; - - fail: - ssl_error(errstr); - fatalx("%s", errstr); -} - -#if defined(SUPPORT_ECDSA) -static void -ecdsa_engine_init(void) -{ - ENGINE *e; - const char *errstr, *name; - - if ((ecdsae_method = ECDSA_METHOD_new_temporary("ECDSA privsep engine", 0)) == NULL) { - errstr = "ECDSA_METHOD_new_temporary"; - goto fail; - } - - ecdsae_method->ecdsa_do_sign = ecdsae_do_sign; - ecdsae_method->ecdsa_sign_setup = ecdsae_sign_setup; - ecdsae_method->ecdsa_do_verify = ecdsae_do_verify; - - if ((e = ENGINE_get_default_ECDSA()) == NULL) { - if ((e = ENGINE_new()) == NULL) { - errstr = "ENGINE_new"; - goto fail; - } - if (!ENGINE_set_name(e, ecdsae_method->name)) { - errstr = "ENGINE_set_name"; - goto fail; - } - if ((ecdsa_default = ECDSA_get_default_method()) == NULL) { - errstr = "ECDSA_get_default_method"; - goto fail; - } - } else if ((ecdsa_default = ENGINE_get_ECDSA(e)) == NULL) { - errstr = "ENGINE_get_ECDSA"; - goto fail; - } - - if ((name = ENGINE_get_name(e)) == NULL) - name = "unknown ECDSA engine"; - - log_debug("debug: %s: using %s", __func__, name); - - if (!ENGINE_set_ECDSA(e, ecdsae_method)) { - errstr = "ENGINE_set_ECDSA"; - goto fail; - } - if (!ENGINE_set_default_ECDSA(e)) { - errstr = "ENGINE_set_default_ECDSA"; - goto fail; - } - - return; - - fail: - ssl_error(errstr); - fatalx("%s", errstr); -} -#endif - -void -ca_engine_init(void) -{ - rsa_engine_init(); -#if defined(SUPPORT_ECDSA) - ecdsa_engine_init(); -#endif -} diff --git a/smtpd/cert.c b/smtpd/cert.c deleted file mode 100644 index 79b1df91..00000000 --- a/smtpd/cert.c +++ /dev/null @@ -1,416 +0,0 @@ -/* $OpenBSD: cert.c,v 1.2 2018/12/11 07:25:57 eric Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/queue.h> -#include <netinet/in.h> - -#include <imsg.h> -#include <limits.h> -#include <openssl/err.h> -#include <openssl/ssl.h> -#include <stdio.h> -#include <string.h> - -#include "log.h" -#include "smtpd.h" -#include "ssl.h" - -#define p_cert p_lka - -struct request { - SPLAY_ENTRY(request) entry; - uint32_t id; - void (*cb_get_certificate)(void *, int, const char *, - const void *, size_t); - void (*cb_verify)(void *, int); - void *arg; -}; - -#define MAX_CERTS 16 -#define MAX_CERT_LEN (MAX_IMSGSIZE - (IMSG_HEADER_SIZE + sizeof(size_t))) - -struct session { - SPLAY_ENTRY(session) entry; - uint32_t id; - struct mproc *proc; - char *cert[MAX_CERTS]; - size_t cert_len[MAX_CERTS]; - int cert_count; -}; - -SPLAY_HEAD(cert_reqtree, request); -SPLAY_HEAD(cert_sestree, session); - -static int request_cmp(struct request *, struct request *); -static int session_cmp(struct session *, struct session *); -SPLAY_PROTOTYPE(cert_reqtree, request, entry, request_cmp); -SPLAY_PROTOTYPE(cert_sestree, session, entry, session_cmp); - -static void cert_do_verify(struct session *, const char *, int); -static int cert_X509_verify(struct session *, const char *, const char *); - -static struct cert_reqtree reqs = SPLAY_INITIALIZER(&reqs); -static struct cert_sestree sess = SPLAY_INITIALIZER(&sess); - -int -cert_init(const char *name, int fallback, void (*cb)(void *, int, - const char *, const void *, size_t), void *arg) -{ - struct request *req; - - req = calloc(1, sizeof(*req)); - if (req == NULL) { - cb(arg, CA_FAIL, NULL, NULL, 0); - return 0; - } - while (req->id == 0 || SPLAY_FIND(cert_reqtree, &reqs, req)) - req->id = arc4random(); - req->cb_get_certificate = cb; - req->arg = arg; - SPLAY_INSERT(cert_reqtree, &reqs, req); - - m_create(p_cert, IMSG_CERT_INIT, req->id, 0, -1); - m_add_string(p_cert, name); - m_add_int(p_cert, fallback); - m_close(p_cert); - - return 1; -} - -int -cert_verify(const void *ssl, const char *name, int fallback, - void (*cb)(void *, int), void *arg) -{ - struct request *req; - X509 *x; - STACK_OF(X509) *xchain; - unsigned char *cert_der[MAX_CERTS]; - int cert_len[MAX_CERTS]; - int i, cert_count, ret; - - x = SSL_get_peer_certificate(ssl); - if (x == NULL) { - cb(arg, CERT_NOCERT); - return 0; - } - - ret = 0; - memset(cert_der, 0, sizeof(cert_der)); - - req = calloc(1, sizeof(*req)); - if (req == NULL) - goto end; - while (req->id == 0 || SPLAY_FIND(cert_reqtree, &reqs, req)) - req->id = arc4random(); - req->cb_verify = cb; - req->arg = arg; - SPLAY_INSERT(cert_reqtree, &reqs, req); - - cert_count = 1; - if ((xchain = SSL_get_peer_cert_chain(ssl))) { - cert_count += sk_X509_num(xchain); - if (cert_count > MAX_CERTS) { - log_warnx("warn: certificate chain too long"); - goto end; - } - } - - for (i = 0; i < cert_count; ++i) { - if (i != 0) { - if ((x = sk_X509_value(xchain, i - 1)) == NULL) { - log_warnx("warn: failed to retrieve certificate"); - goto end; - } - } - - cert_len[i] = i2d_X509(x, &cert_der[i]); - if (i == 0) - X509_free(x); - - if (cert_len[i] < 0) { - log_warnx("warn: failed to encode certificate"); - goto end; - } - - log_debug("debug: certificate %i: len=%d", i, cert_len[i]); - if (cert_len[i] > (int)MAX_CERT_LEN) { - log_warnx("warn: certificate too long"); - goto end; - } - } - - /* Send the cert chain, one cert at a time */ - for (i = 0; i < cert_count; ++i) { - m_create(p_cert, IMSG_CERT_CERTIFICATE, req->id, 0, -1); - m_add_data(p_cert, cert_der[i], cert_len[i]); - m_close(p_cert); - } - - /* Tell lookup process that it can start verifying, we're done */ - m_create(p_cert, IMSG_CERT_VERIFY, req->id, 0, -1); - m_add_string(p_cert, name); - m_add_int(p_cert, fallback); - m_close(p_cert); - - ret = 1; - - end: - for (i = 0; i < MAX_CERTS; ++i) - free(cert_der[i]); - - if (ret == 0) { - if (req) - SPLAY_REMOVE(cert_reqtree, &reqs, req); - free(req); - cb(arg, CERT_ERROR); - } - - return ret; -} - - -void -cert_dispatch_request(struct mproc *proc, struct imsg *imsg) -{ - struct pki *pki; - struct session key, *s; - const char *name; - const void *data; - size_t datalen; - struct msg m; - uint32_t reqid; - char buf[LINE_MAX]; - int fallback; - - reqid = imsg->hdr.peerid; - m_msg(&m, imsg); - - switch (imsg->hdr.type) { - - case IMSG_CERT_INIT: - m_get_string(&m, &name); - m_get_int(&m, &fallback); - m_end(&m); - - xlowercase(buf, name, sizeof(buf)); - log_debug("debug: looking up pki \"%s\"", buf); - pki = dict_get(env->sc_pki_dict, buf); - if (pki == NULL && fallback) - pki = dict_get(env->sc_pki_dict, "*"); - - m_create(proc, IMSG_CERT_INIT, reqid, 0, -1); - if (pki) { - m_add_int(proc, CA_OK); - m_add_string(proc, pki->pki_name); - m_add_data(proc, pki->pki_cert, pki->pki_cert_len); - } else { - m_add_int(proc, CA_FAIL); - m_add_string(proc, NULL); - m_add_data(proc, NULL, 0); - } - m_close(proc); - return; - - case IMSG_CERT_CERTIFICATE: - m_get_data(&m, &data, &datalen); - m_end(&m); - - key.id = reqid; - key.proc = proc; - s = SPLAY_FIND(cert_sestree, &sess, &key); - if (s == NULL) { - s = calloc(1, sizeof(*s)); - s->proc = proc; - s->id = reqid; - SPLAY_INSERT(cert_sestree, &sess, s); - } - - if (s->cert_count == MAX_CERTS) - fatalx("%s: certificate chain too long", __func__); - - s->cert[s->cert_count] = xmemdup(data, datalen); - s->cert_len[s->cert_count] = datalen; - s->cert_count++; - return; - - case IMSG_CERT_VERIFY: - m_get_string(&m, &name); - m_get_int(&m, &fallback); - m_end(&m); - - key.id = reqid; - key.proc = proc; - s = SPLAY_FIND(cert_sestree, &sess, &key); - if (s == NULL) - fatalx("%s: no certificate", __func__); - - SPLAY_REMOVE(cert_sestree, &sess, s); - cert_do_verify(s, name, fallback); - return; - - default: - fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type)); - } -} - -void -cert_dispatch_result(struct mproc *proc, struct imsg *imsg) -{ - struct request key, *req; - struct msg m; - const void *cert; - const char *name; - size_t cert_len; - int res; - - key.id = imsg->hdr.peerid; - req = SPLAY_FIND(cert_reqtree, &reqs, &key); - if (req == NULL) - fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid); - - m_msg(&m, imsg); - - switch (imsg->hdr.type) { - - case IMSG_CERT_INIT: - m_get_int(&m, &res); - m_get_string(&m, &name); - m_get_data(&m, &cert, &cert_len); - m_end(&m); - SPLAY_REMOVE(cert_reqtree, &reqs, req); - req->cb_get_certificate(req->arg, res, name, cert, cert_len); - free(req); - break; - - case IMSG_CERT_VERIFY: - m_get_int(&m, &res); - m_end(&m); - SPLAY_REMOVE(cert_reqtree, &reqs, req); - req->cb_verify(req->arg, res); - free(req); - break; - } -} - -static void -cert_do_verify(struct session *s, const char *name, int fallback) -{ - struct ca *ca; - const char *cafile; - int i, res; - - ca = dict_get(env->sc_ca_dict, name); - if (ca == NULL) - if (fallback) - ca = dict_get(env->sc_ca_dict, "*"); - cafile = ca ? ca->ca_cert_file : CA_FILE; - - if (ca == NULL && !fallback) - res = CERT_NOCA; - else if (!cert_X509_verify(s, cafile, NULL)) - res = CERT_INVALID; - else - res = CERT_OK; - - for (i = 0; i < s->cert_count; ++i) - free(s->cert[i]); - - m_create(s->proc, IMSG_CERT_VERIFY, s->id, 0, -1); - m_add_int(s->proc, res); - m_close(s->proc); - - free(s); -} - -static int -cert_X509_verify(struct session *s, const char *CAfile, - const char *CRLfile) -{ - X509 *x509; - X509 *x509_tmp; - STACK_OF(X509) *x509_chain; - const unsigned char *d2i; - int i, ret = 0; - const char *errstr; - - x509 = NULL; - x509_tmp = NULL; - x509_chain = NULL; - - d2i = s->cert[0]; - if (d2i_X509(&x509, &d2i, s->cert_len[0]) == NULL) { - x509 = NULL; - goto end; - } - - if (s->cert_count > 1) { - x509_chain = sk_X509_new_null(); - for (i = 1; i < s->cert_count; ++i) { - d2i = s->cert[i]; - if (d2i_X509(&x509_tmp, &d2i, s->cert_len[i]) == NULL) - goto end; - sk_X509_insert(x509_chain, x509_tmp, i); - x509_tmp = NULL; - } - } - if (!ca_X509_verify(x509, x509_chain, CAfile, NULL, &errstr)) - log_debug("debug: X509 verify: %s", errstr); - else - ret = 1; - -end: - X509_free(x509); - X509_free(x509_tmp); - if (x509_chain) - sk_X509_pop_free(x509_chain, X509_free); - - return ret; -} - -static int -request_cmp(struct request *a, struct request *b) -{ - if (a->id < b->id) - return -1; - if (a->id > b->id) - return 1; - return 0; -} - -SPLAY_GENERATE(cert_reqtree, request, entry, request_cmp); - -static int -session_cmp(struct session *a, struct session *b) -{ - if (a->id < b->id) - return -1; - if (a->id > b->id) - return 1; - if (a->proc < b->proc) - return -1; - if (a->proc > b->proc) - return 1; - return 0; -} - -SPLAY_GENERATE(cert_sestree, session, entry, session_cmp); diff --git a/smtpd/compress_backend.c b/smtpd/compress_backend.c deleted file mode 100644 index 1b974662..00000000 --- a/smtpd/compress_backend.c +++ /dev/null @@ -1,72 +0,0 @@ -/* $OpenBSD: compress_backend.c,v 1.9 2015/01/20 17:37:54 deraadt Exp $ */ - -/* - * Copyright (c) 2012 Charles Longeau <chl@openbsd.org> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" - -#define BUFFER_SIZE 16364 - -extern struct compress_backend compress_gzip; - -struct compress_backend * -compress_backend_lookup(const char *name) -{ - if (!strcmp(name, "gzip")) - return &compress_gzip; - - return NULL; -} - -size_t -compress_chunk(void *ib, size_t ibsz, void *ob, size_t obsz) -{ - return (env->sc_comp->compress_chunk(ib, ibsz, ob, obsz)); -} - -size_t -uncompress_chunk(void *ib, size_t ibsz, void *ob, size_t obsz) -{ - return (env->sc_comp->uncompress_chunk(ib, ibsz, ob, obsz)); -} - -int -compress_file(FILE *ifile, FILE *ofile) -{ - return (env->sc_comp->compress_file(ifile, ofile)); -} - -int -uncompress_file(FILE *ifile, FILE *ofile) -{ - return (env->sc_comp->uncompress_file(ifile, ofile)); -} diff --git a/smtpd/compress_gzip.c b/smtpd/compress_gzip.c deleted file mode 100644 index dd60aeec..00000000 --- a/smtpd/compress_gzip.c +++ /dev/null @@ -1,186 +0,0 @@ -/* $OpenBSD: compress_gzip.c,v 1.10 2015/12/28 22:08:30 jung Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Charles Longeau <chl@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <fcntl.h> -#include <imsg.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include <zlib.h> - -#include "smtpd.h" -#include "log.h" - - -#define GZIP_BUFFER_SIZE 16384 - - -static size_t compress_gzip_chunk(void *, size_t, void *, size_t); -static size_t uncompress_gzip_chunk(void *, size_t, void *, size_t); -static int compress_gzip_file(FILE *, FILE *); -static int uncompress_gzip_file(FILE *, FILE *); - - -struct compress_backend compress_gzip = { - compress_gzip_chunk, - uncompress_gzip_chunk, - - compress_gzip_file, - uncompress_gzip_file, -}; - -static size_t -compress_gzip_chunk(void *ib, size_t ibsz, void *ob, size_t obsz) -{ - z_stream *strm; - size_t ret = 0; - - if ((strm = calloc(1, sizeof *strm)) == NULL) - return 0; - - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - if (deflateInit2(strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) - goto end; - - strm->avail_in = ibsz; - strm->next_in = (unsigned char *)ib; - strm->avail_out = obsz; - strm->next_out = (unsigned char *)ob; - if (deflate(strm, Z_FINISH) != Z_STREAM_END) - goto end; - - ret = strm->total_out; - -end: - deflateEnd(strm); - free(strm); - return ret; -} - - -static size_t -uncompress_gzip_chunk(void *ib, size_t ibsz, void *ob, size_t obsz) -{ - z_stream *strm; - size_t ret = 0; - - if ((strm = calloc(1, sizeof *strm)) == NULL) - return 0; - - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - strm->avail_in = 0; - strm->next_in = Z_NULL; - - if (inflateInit2(strm, (15+16)) != Z_OK) - goto end; - - strm->avail_in = ibsz; - strm->next_in = (unsigned char *)ib; - strm->avail_out = obsz; - strm->next_out = (unsigned char *)ob; - - if (inflate(strm, Z_FINISH) != Z_STREAM_END) - goto end; - - ret = strm->total_out; - -end: - deflateEnd(strm); - free(strm); - return ret; -} - - -static int -compress_gzip_file(FILE *in, FILE *out) -{ - gzFile gzf; - char ibuf[GZIP_BUFFER_SIZE]; - int r, w; - int ret = 0; - - if (in == NULL || out == NULL) - return (0); - - gzf = gzdopen(fileno(out), "wb"); - if (gzf == NULL) - return (0); - - while ((r = fread(ibuf, 1, GZIP_BUFFER_SIZE, in)) != 0) { - if ((w = gzwrite(gzf, ibuf, r)) != r) - goto end; - } - if (!feof(in)) - goto end; - - ret = 1; - -end: - gzclose(gzf); - return (ret); -} - - -static int -uncompress_gzip_file(FILE *in, FILE *out) -{ - gzFile gzf; - char obuf[GZIP_BUFFER_SIZE]; - int r, w; - int ret = 0; - - if (in == NULL || out == NULL) - return (0); - - gzf = gzdopen(fileno(in), "r"); - if (gzf == NULL) - return (0); - - while ((r = gzread(gzf, obuf, sizeof(obuf))) > 0) { - if ((w = fwrite(obuf, r, 1, out)) != 1) - goto end; - } - if (!gzeof(gzf)) - goto end; - - ret = 1; - -end: - gzclose(gzf); - return (ret); -} diff --git a/smtpd/config.c b/smtpd/config.c deleted file mode 100644 index 8fe983d6..00000000 --- a/smtpd/config.c +++ /dev/null @@ -1,350 +0,0 @@ -/* $OpenBSD: config.c,v 1.51 2019/12/18 10:00:39 gilles Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/resource.h> - -#include <event.h> -#include <ifaddrs.h> -#include <imsg.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <string.h> -#include <unistd.h> - -#include <openssl/ssl.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -void set_local(struct smtpd *, const char *); -void set_localaddrs(struct smtpd *, struct table *); - -struct smtpd * -config_default(void) -{ - struct smtpd *conf = NULL; - struct mta_limits *limits = NULL; - struct table *t = NULL; - char hostname[HOST_NAME_MAX+1]; - - if (getmailname(hostname, sizeof hostname) == -1) - return NULL; - - if ((conf = calloc(1, sizeof(*conf))) == NULL) - return conf; - - (void)strlcpy(conf->sc_hostname, hostname, sizeof(conf->sc_hostname)); - - conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE; - conf->sc_subaddressing_delim = SUBADDRESSING_DELIMITER; - conf->sc_ttl = SMTPD_QUEUE_EXPIRY; - conf->sc_srs_ttl = SMTPD_QUEUE_EXPIRY / 86400; - - conf->sc_mta_max_deferred = 100; - conf->sc_scheduler_max_inflight = 5000; - conf->sc_scheduler_max_schedule = 10; - conf->sc_scheduler_max_evp_batch_size = 256; - conf->sc_scheduler_max_msg_batch_size = 1024; - - conf->sc_session_max_rcpt = 1000; - conf->sc_session_max_mails = 100; - - conf->sc_mda_max_session = 50; - conf->sc_mda_max_user_session = 7; - conf->sc_mda_task_hiwat = 50; - conf->sc_mda_task_lowat = 30; - conf->sc_mda_task_release = 10; - - /* Report mails delayed for more than 4 hours */ - conf->sc_bounce_warn[0] = 3600 * 4; - - conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict)); - conf->sc_rules = calloc(1, sizeof(*conf->sc_rules)); - conf->sc_dispatchers = calloc(1, sizeof(*conf->sc_dispatchers)); - conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners)); - conf->sc_ca_dict = calloc(1, sizeof(*conf->sc_ca_dict)); - conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict)); - conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict)); - conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict)); - conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers)); - conf->sc_filter_processes_dict = calloc(1, sizeof(*conf->sc_filter_processes_dict)); - conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce)); - conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict)); - limits = calloc(1, sizeof(*limits)); - - if (conf->sc_tables_dict == NULL || - conf->sc_rules == NULL || - conf->sc_dispatchers == NULL || - conf->sc_listeners == NULL || - conf->sc_ca_dict == NULL || - conf->sc_pki_dict == NULL || - conf->sc_ssl_dict == NULL || - conf->sc_limits_dict == NULL || - conf->sc_mda_wrappers == NULL || - conf->sc_filter_processes_dict == NULL || - conf->sc_dispatcher_bounce == NULL || - conf->sc_filters_dict == NULL || - limits == NULL) - goto error; - - dict_init(conf->sc_dispatchers); - dict_init(conf->sc_mda_wrappers); - dict_init(conf->sc_ca_dict); - dict_init(conf->sc_pki_dict); - dict_init(conf->sc_ssl_dict); - dict_init(conf->sc_tables_dict); - dict_init(conf->sc_limits_dict); - dict_init(conf->sc_filter_processes_dict); - - limit_mta_set_defaults(limits); - - dict_xset(conf->sc_limits_dict, "default", limits); - - TAILQ_INIT(conf->sc_listeners); - TAILQ_INIT(conf->sc_rules); - - - /* bounce dispatcher */ - conf->sc_dispatcher_bounce->type = DISPATCHER_BOUNCE; - - /* - * declare special "localhost", "anyhost" and "localnames" tables - */ - set_local(conf, conf->sc_hostname); - - t = table_create(conf, "static", "<anydestination>", NULL); - table_add(t, "*", NULL); - - hostname[strcspn(hostname, ".")] = '\0'; - if (strcmp(conf->sc_hostname, hostname) != 0) - table_add(t, hostname, NULL); - - table_create(conf, "getpwnam", "<getpwnam>", NULL); - - return conf; - -error: - free(conf->sc_tables_dict); - free(conf->sc_rules); - free(conf->sc_dispatchers); - free(conf->sc_listeners); - free(conf->sc_ca_dict); - free(conf->sc_pki_dict); - free(conf->sc_ssl_dict); - free(conf->sc_limits_dict); - free(conf->sc_mda_wrappers); - free(conf->sc_filter_processes_dict); - free(conf->sc_dispatcher_bounce); - free(conf->sc_filters_dict); - free(limits); - free(conf); - return NULL; -} - -void -set_local(struct smtpd *conf, const char *hostname) -{ - struct table *t; - - t = table_create(conf, "static", "<localnames>", NULL); - table_add(t, "localhost", NULL); - table_add(t, hostname, NULL); - - set_localaddrs(conf, t); -} - -void -set_localaddrs(struct smtpd *conf, struct table *localnames) -{ - struct ifaddrs *ifap, *p; - struct sockaddr_storage ss; - struct sockaddr_in *sain; - struct sockaddr_in6 *sin6; - struct table *t; - char buf[NI_MAXHOST + 5]; - - t = table_create(conf, "static", "<anyhost>", NULL); - table_add(t, "local", NULL); - table_add(t, "0.0.0.0/0", NULL); - table_add(t, "::/0", NULL); - - if (getifaddrs(&ifap) == -1) - fatal("getifaddrs"); - - t = table_create(conf, "static", "<localhost>", NULL); - table_add(t, "local", NULL); - - for (p = ifap; p != NULL; p = p->ifa_next) { - if (p->ifa_addr == NULL) - continue; - switch (p->ifa_addr->sa_family) { - case AF_INET: - sain = (struct sockaddr_in *)&ss; - *sain = *(struct sockaddr_in *)p->ifa_addr; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sain->sin_len = sizeof(struct sockaddr_in); -#endif - table_add(t, ss_to_text(&ss), NULL); - table_add(localnames, ss_to_text(&ss), NULL); - (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss)); - table_add(localnames, buf, NULL); - break; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *)&ss; - *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - table_add(t, ss_to_text(&ss), NULL); - table_add(localnames, ss_to_text(&ss), NULL); - (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss)); - table_add(localnames, buf, NULL); - (void)snprintf(buf, sizeof buf, "[ipv6:%s]", ss_to_text(&ss)); - table_add(localnames, buf, NULL); - break; - } - } - - freeifaddrs(ifap); -} - -void -purge_config(uint8_t what) -{ - struct dispatcher *d; - struct listener *l; - struct table *t; - struct rule *r; - struct pki *p; - const char *k; - void *iter_dict; - - if (what & PURGE_LISTENERS) { - while ((l = TAILQ_FIRST(env->sc_listeners)) != NULL) { - TAILQ_REMOVE(env->sc_listeners, l, entry); - free(l); - } - free(env->sc_listeners); - env->sc_listeners = NULL; - } - if (what & PURGE_TABLES) { - while (dict_root(env->sc_tables_dict, NULL, (void **)&t)) - table_destroy(env, t); - free(env->sc_tables_dict); - env->sc_tables_dict = NULL; - } - if (what & PURGE_RULES) { - while ((r = TAILQ_FIRST(env->sc_rules)) != NULL) { - TAILQ_REMOVE(env->sc_rules, r, r_entry); - free(r); - } - free(env->sc_rules); - env->sc_rules = NULL; - } - if (what & PURGE_DISPATCHERS) { - while (dict_poproot(env->sc_dispatchers, (void **)&d)) { - free(d); - } - free(env->sc_dispatchers); - env->sc_dispatchers = NULL; - } - if (what & PURGE_PKI) { - while (dict_poproot(env->sc_pki_dict, (void **)&p)) { - freezero(p->pki_cert, p->pki_cert_len); - freezero(p->pki_key, p->pki_key_len); - EVP_PKEY_free(p->pki_pkey); - free(p); - } - free(env->sc_pki_dict); - env->sc_pki_dict = NULL; - } else if (what & PURGE_PKI_KEYS) { - iter_dict = NULL; - while (dict_iter(env->sc_pki_dict, &iter_dict, &k, - (void **)&p)) { - freezero(p->pki_cert, p->pki_cert_len); - p->pki_cert = NULL; - freezero(p->pki_key, p->pki_key_len); - p->pki_key = NULL; - EVP_PKEY_free(p->pki_pkey); - p->pki_pkey = NULL; - } - } -} - -#ifndef CONFIG_MINIMUM - -void -config_process(enum smtp_proc_type proc) -{ - struct rlimit rl; - - smtpd_process = proc; - setproctitle("%s", proc_title(proc)); - - if (getrlimit(RLIMIT_NOFILE, &rl) == -1) - fatal("fdlimit: getrlimit"); - rl.rlim_cur = rl.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rl) == -1) - if (errno != EINVAL) - fatal("fdlimit: setrlimit"); -} - -void -config_peer(enum smtp_proc_type proc) -{ - struct mproc *p; - - if (proc == smtpd_process) - fatal("config_peers: cannot peer with oneself"); - - if (proc == PROC_CONTROL) - p = p_control; - else if (proc == PROC_LKA) - p = p_lka; - else if (proc == PROC_PARENT) - p = p_parent; - else if (proc == PROC_QUEUE) - p = p_queue; - else if (proc == PROC_SCHEDULER) - p = p_scheduler; - else if (proc == PROC_PONY) - p = p_pony; - else if (proc == PROC_CA) - p = p_ca; - else - fatalx("bad peer"); - - mproc_enable(p); -} - -#else - -void config_process(enum smtp_proc_type proc) {} -void config_peer(enum smtp_proc_type proc) {} - -#endif diff --git a/smtpd/control.c b/smtpd/control.c deleted file mode 100644 index 0e35bbd1..00000000 --- a/smtpd/control.c +++ /dev/null @@ -1,817 +0,0 @@ -/* $OpenBSD: control.c,v 1.123 2018/05/31 21:06:12 gilles Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <grp.h> /* needed for setgroups */ -#include <imsg.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define CONTROL_BACKLOG 5 - -struct ctl_conn { - uint32_t id; - uint8_t flags; -#define CTL_CONN_NOTIFY 0x01 - struct mproc mproc; - uid_t euid; - gid_t egid; -}; - -struct { - struct event ev; - int fd; -} control_state; - -static void control_imsg(struct mproc *, struct imsg *); -static void control_shutdown(void); -static void control_listen(void); -static void control_accept(int, short, void *); -static void control_close(struct ctl_conn *); -static void control_dispatch_ext(struct mproc *, struct imsg *); -static void control_digest_update(const char *, size_t, int); -static void control_broadcast_verbose(int, int); - -static struct stat_backend *stat_backend = NULL; -extern const char *backend_stat; - -static uint64_t connid = 0; -static struct tree ctl_conns; -static struct tree ctl_count; -static struct stat_digest digest; - -#define CONTROL_FD_RESERVE 5 -#define CONTROL_MAXCONN_PER_CLIENT 32 - -static void -control_imsg(struct mproc *p, struct imsg *imsg) -{ - struct ctl_conn *c; - struct stat_value val; - struct msg m; - const char *key; - const void *data; - size_t sz; - - if (imsg == NULL) { - if (p->proc != PROC_CLIENT) - control_shutdown(); - return; - } - - switch (imsg->hdr.type) { - case IMSG_CTL_OK: - case IMSG_CTL_FAIL: - case IMSG_CTL_LIST_MESSAGES: - case IMSG_CTL_LIST_ENVELOPES: - case IMSG_CTL_DISCOVER_EVPID: - case IMSG_CTL_DISCOVER_MSGID: - case IMSG_CTL_MTA_SHOW_HOSTS: - case IMSG_CTL_MTA_SHOW_RELAYS: - case IMSG_CTL_MTA_SHOW_ROUTES: - case IMSG_CTL_MTA_SHOW_HOSTSTATS: - case IMSG_CTL_MTA_SHOW_BLOCK: - c = tree_get(&ctl_conns, imsg->hdr.peerid); - if (c == NULL) - return; - imsg->hdr.peerid = 0; - m_forward(&c->mproc, imsg); - return; - - case IMSG_CTL_SMTP_SESSION: - c = tree_get(&ctl_conns, imsg->hdr.peerid); - if (c == NULL) - return; - m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg->fd, NULL, 0); - return; - - case IMSG_STAT_INCREMENT: - m_msg(&m, imsg); - m_get_string(&m, &key); - m_get_data(&m, &data, &sz); - m_end(&m); - if (sz != sizeof(val)) - fatalx("control: IMSG_STAT_INCREMENT size mismatch"); - memmove(&val, data, sz); - if (stat_backend) - stat_backend->increment(key, val.u.counter); - control_digest_update(key, val.u.counter, 1); - return; - - case IMSG_STAT_DECREMENT: - m_msg(&m, imsg); - m_get_string(&m, &key); - m_get_data(&m, &data, &sz); - m_end(&m); - if (sz != sizeof(val)) - fatalx("control: IMSG_STAT_DECREMENT size mismatch"); - memmove(&val, data, sz); - if (stat_backend) - stat_backend->decrement(key, val.u.counter); - control_digest_update(key, val.u.counter, 0); - return; - - case IMSG_STAT_SET: - m_msg(&m, imsg); - m_get_string(&m, &key); - m_get_data(&m, &data, &sz); - m_end(&m); - if (sz != sizeof(val)) - fatalx("control: IMSG_STAT_SET size mismatch"); - memmove(&val, data, sz); - if (stat_backend) - stat_backend->set(key, &val); - return; - } - - errx(1, "control_imsg: unexpected %s imsg", - imsg_to_str(imsg->hdr.type)); -} - -int -control_create_socket(void) -{ - struct sockaddr_un s_un; - int fd; - mode_t old_umask; - - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("control: socket"); - - memset(&s_un, 0, sizeof(s_un)); - s_un.sun_family = AF_UNIX; - if (strlcpy(s_un.sun_path, SMTPD_SOCKET, - sizeof(s_un.sun_path)) >= sizeof(s_un.sun_path)) - fatal("control: socket name too long"); - - if (connect(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) - fatalx("control socket already listening"); - - if (unlink(SMTPD_SOCKET) == -1) - if (errno != ENOENT) - fatal("control: cannot unlink socket"); - - old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); - if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { - (void)umask(old_umask); - fatal("control: bind"); - } - (void)umask(old_umask); - - if (chmod(SMTPD_SOCKET, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { - (void)unlink(SMTPD_SOCKET); - fatal("control: chmod"); - } - - io_set_nonblocking(fd); - control_state.fd = fd; - - return fd; -} - -int -control(void) -{ - struct passwd *pw; - - purge_config(PURGE_EVERYTHING); - - if ((pw = getpwnam(SMTPD_USER)) == NULL) - fatalx("unknown user " SMTPD_USER); - - stat_backend = env->sc_stat; - stat_backend->init(); - - if (chroot(PATH_CHROOT) == -1) - fatal("control: chroot"); - if (chdir("/") == -1) - fatal("control: chdir(\"/\")"); - - config_process(PROC_CONTROL); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("control: cannot drop privileges"); - - imsg_callback = control_imsg; - event_init(); - - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - - tree_init(&ctl_conns); - tree_init(&ctl_count); - - memset(&digest, 0, sizeof digest); - digest.startup = time(NULL); - - config_peer(PROC_SCHEDULER); - config_peer(PROC_QUEUE); - config_peer(PROC_PARENT); - config_peer(PROC_LKA); - config_peer(PROC_PONY); - config_peer(PROC_CA); - - control_listen(); - -#if HAVE_PLEDGE - if (pledge("stdio unix recvfd sendfd", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} - -static void -control_shutdown(void) -{ - log_debug("debug: control agent exiting"); - _exit(0); -} - -static void -control_listen(void) -{ - if (listen(control_state.fd, CONTROL_BACKLOG) == -1) - fatal("control_listen"); - - event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST, - control_accept, NULL); - event_add(&control_state.ev, NULL); -} - -/* ARGSUSED */ -static void -control_accept(int listenfd, short event, void *arg) -{ - int connfd; - socklen_t len; - struct sockaddr_un s_un; - struct ctl_conn *c; - size_t *count; - uid_t euid; - gid_t egid; - -#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT) - if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) - goto pause; -#else - if (available_fds(CONTROL_FD_RESERVE)) - goto pause; -#endif - - len = sizeof(s_un); - if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) { - if (errno == ENFILE || errno == EMFILE) - goto pause; - if (errno == EINTR || errno == EWOULDBLOCK || - errno == ECONNABORTED) - return; - fatal("control_accept: accept"); - } - - io_set_nonblocking(connfd); - - if (getpeereid(connfd, &euid, &egid) == -1) - fatal("getpeereid"); - - count = tree_get(&ctl_count, euid); - if (count == NULL) { - count = xcalloc(1, sizeof *count); - tree_xset(&ctl_count, euid, count); - } - - if (*count == CONTROL_MAXCONN_PER_CLIENT) { - close(connfd); - log_warnx("warn: too many connections to control socket " - "from user with uid %lu", (unsigned long int)euid); - return; - } - (*count)++; - - do { - ++connid; - } while (tree_get(&ctl_conns, connid)); - - c = xcalloc(1, sizeof(*c)); - c->euid = euid; - c->egid = egid; - c->id = connid; - c->mproc.proc = PROC_CLIENT; - c->mproc.handler = control_dispatch_ext; - c->mproc.data = c; - if ((c->mproc.name = strdup(proc_title(c->mproc.proc))) == NULL) - fatal("strdup"); - mproc_init(&c->mproc, connfd); - mproc_enable(&c->mproc); - tree_xset(&ctl_conns, c->id, c); - - stat_backend->increment("control.session", 1); - return; - -pause: - log_warnx("warn: ctl client limit hit, disabling new connections"); - event_del(&control_state.ev); -} - -static void -control_close(struct ctl_conn *c) -{ - size_t *count; - - count = tree_xget(&ctl_count, c->euid); - (*count)--; - if (*count == 0) { - tree_xpop(&ctl_count, c->euid); - free(count); - } - tree_xpop(&ctl_conns, c->id); - mproc_clear(&c->mproc); - free(c); - - stat_backend->decrement("control.session", 1); - -#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT) - if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) - return; -#else - if (available_fds(CONTROL_FD_RESERVE)) - return; -#endif - - if (!event_pending(&control_state.ev, EV_READ, NULL)) { - log_warnx("warn: re-enabling ctl connections"); - event_add(&control_state.ev, NULL); - } -} - -static void -control_digest_update(const char *key, size_t value, int incr) -{ - size_t *p; - - p = NULL; - - if (!strcmp(key, "smtp.session")) { - if (incr) - p = &digest.clt_connect; - else - digest.clt_disconnect += value; - } - else if (!strcmp(key, "scheduler.envelope")) { - if (incr) - p = &digest.evp_enqueued; - else - digest.evp_dequeued += value; - } - else if (!strcmp(key, "scheduler.envelope.expired")) - p = &digest.evp_expired; - else if (!strcmp(key, "scheduler.envelope.removed")) - p = &digest.evp_removed; - else if (!strcmp(key, "scheduler.delivery.ok")) - p = &digest.dlv_ok; - else if (!strcmp(key, "scheduler.delivery.permfail")) - p = &digest.dlv_permfail; - else if (!strcmp(key, "scheduler.delivery.tempfail")) - p = &digest.dlv_tempfail; - else if (!strcmp(key, "scheduler.delivery.loop")) - p = &digest.dlv_loop; - - else if (!strcmp(key, "queue.bounce")) - p = &digest.evp_bounce; - - if (p) { - if (incr) - *p = *p + value; - else - *p = *p - value; - } -} - -/* ARGSUSED */ -static void -control_dispatch_ext(struct mproc *p, struct imsg *imsg) -{ - struct sockaddr_storage ss; - struct ctl_conn *c; - int v; - struct stat_kv *kvp; - char *key; - struct stat_value val; - size_t len; - uint64_t evpid; - uint32_t msgid; - - c = p->data; - - if (imsg == NULL) { - control_close(c); - return; - } - - if (imsg->hdr.peerid != IMSG_VERSION) { - m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0); - return; - } - - switch (imsg->hdr.type) { - case IMSG_CTL_SMTP_SESSION: - if (env->sc_flags & SMTPD_SMTP_PAUSED) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - m_compose(p_pony, IMSG_CTL_SMTP_SESSION, c->id, 0, -1, - &c->euid, sizeof(c->euid)); - return; - - case IMSG_CTL_GET_DIGEST: - if (c->euid) - goto badcred; - digest.timestamp = time(NULL); - m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest); - return; - - case IMSG_CTL_GET_STATS: - if (c->euid) - goto badcred; - kvp = imsg->data; - if (!stat_backend->iter(&kvp->iter, &key, &val)) - kvp->iter = NULL; - else { - (void)strlcpy(kvp->key, key, sizeof kvp->key); - kvp->val = val; - } - m_compose(p, IMSG_CTL_GET_STATS, 0, 0, -1, kvp, sizeof *kvp); - return; - - case IMSG_CTL_VERBOSE: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) - goto badcred; - - memcpy(&v, imsg->data, sizeof(v)); - log_trace_verbose(v); - - control_broadcast_verbose(IMSG_CTL_VERBOSE, v); - - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_TRACE_ENABLE: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) - goto badcred; - - memcpy(&v, imsg->data, sizeof(v)); - tracing |= v; - log_trace_verbose(tracing); - - control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing); - - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_TRACE_DISABLE: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) - goto badcred; - - memcpy(&v, imsg->data, sizeof(v)); - tracing &= ~v; - log_trace_verbose(tracing); - - control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing); - - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_PROFILE_ENABLE: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) - goto badcred; - - memcpy(&v, imsg->data, sizeof(v)); - profiling |= v; - - control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); - - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_PROFILE_DISABLE: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) - goto badcred; - - memcpy(&v, imsg->data, sizeof(v)); - profiling &= ~v; - - control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); - - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_PAUSE_EVP: - if (c->euid) - goto badcred; - - imsg->hdr.peerid = c->id; - m_forward(p_scheduler, imsg); - return; - - case IMSG_CTL_PAUSE_MDA: - if (c->euid) - goto badcred; - - if (env->sc_flags & SMTPD_MDA_PAUSED) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - log_info("info: mda paused"); - env->sc_flags |= SMTPD_MDA_PAUSED; - m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_PAUSE_MTA: - if (c->euid) - goto badcred; - - if (env->sc_flags & SMTPD_MTA_PAUSED) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - log_info("info: mta paused"); - env->sc_flags |= SMTPD_MTA_PAUSED; - m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_PAUSE_SMTP: - if (c->euid) - goto badcred; - - if (env->sc_flags & SMTPD_SMTP_PAUSED) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - log_info("info: smtp paused"); - env->sc_flags |= SMTPD_SMTP_PAUSED; - m_compose(p_pony, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_RESUME_EVP: - if (c->euid) - goto badcred; - - imsg->hdr.peerid = c->id; - m_forward(p_scheduler, imsg); - return; - - case IMSG_CTL_RESUME_MDA: - if (c->euid) - goto badcred; - - if (!(env->sc_flags & SMTPD_MDA_PAUSED)) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - log_info("info: mda resumed"); - env->sc_flags &= ~SMTPD_MDA_PAUSED; - m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_RESUME_MTA: - if (c->euid) - goto badcred; - - if (!(env->sc_flags & SMTPD_MTA_PAUSED)) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - log_info("info: mta resumed"); - env->sc_flags &= ~SMTPD_MTA_PAUSED; - m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_RESUME_SMTP: - if (c->euid) - goto badcred; - - if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) { - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); - return; - } - log_info("info: smtp resumed"); - env->sc_flags &= ~SMTPD_SMTP_PAUSED; - m_forward(p_pony, imsg); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_RESUME_ROUTE: - if (c->euid) - goto badcred; - - m_forward(p_pony, imsg); - m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); - return; - - case IMSG_CTL_LIST_MESSAGES: - if (c->euid) - goto badcred; - m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1, - imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); - return; - - case IMSG_CTL_LIST_ENVELOPES: - if (c->euid) - goto badcred; - m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1, - imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); - return; - - case IMSG_CTL_MTA_SHOW_HOSTS: - case IMSG_CTL_MTA_SHOW_RELAYS: - case IMSG_CTL_MTA_SHOW_ROUTES: - case IMSG_CTL_MTA_SHOW_HOSTSTATS: - case IMSG_CTL_MTA_SHOW_BLOCK: - if (c->euid) - goto badcred; - - imsg->hdr.peerid = c->id; - m_forward(p_pony, imsg); - return; - - case IMSG_CTL_SHOW_STATUS: - if (c->euid) - goto badcred; - - m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags, - sizeof(env->sc_flags)); - return; - - case IMSG_CTL_MTA_BLOCK: - case IMSG_CTL_MTA_UNBLOCK: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss)) - goto invalid; - memmove(&ss, imsg->data, sizeof(ss)); - m_create(p_pony, imsg->hdr.type, c->id, 0, -1); - m_add_sockaddr(p_pony, (struct sockaddr *)&ss); - m_add_string(p_pony, (char *)imsg->data + sizeof(ss)); - m_close(p_pony); - return; - - case IMSG_CTL_SCHEDULE: - if (c->euid) - goto badcred; - - imsg->hdr.peerid = c->id; - m_forward(p_scheduler, imsg); - return; - - case IMSG_CTL_REMOVE: - if (c->euid) - goto badcred; - - imsg->hdr.peerid = c->id; - m_forward(p_scheduler, imsg); - return; - - case IMSG_CTL_UPDATE_TABLE: - if (c->euid) - goto badcred; - - /* table name too long */ - len = strlen(imsg->data); - if (len >= LINE_MAX) - goto invalid; - - imsg->hdr.peerid = c->id; - m_forward(p_lka, imsg); - return; - - case IMSG_CTL_DISCOVER_EVPID: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid) - goto invalid; - - memmove(&evpid, imsg->data, sizeof evpid); - m_create(p_queue, imsg->hdr.type, c->id, 0, -1); - m_add_evpid(p_queue, evpid); - m_close(p_queue); - return; - - case IMSG_CTL_DISCOVER_MSGID: - if (c->euid) - goto badcred; - - if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid) - goto invalid; - - memmove(&msgid, imsg->data, sizeof msgid); - m_create(p_queue, imsg->hdr.type, c->id, 0, -1); - m_add_msgid(p_queue, msgid); - m_close(p_queue); - return; - - default: - log_debug("debug: control_dispatch_ext: " - "error handling %s imsg", - imsg_to_str(imsg->hdr.type)); - return; - } -badcred: -invalid: - m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); -} - -static void -control_broadcast_verbose(int msg, int v) -{ - m_create(p_lka, msg, 0, 0, -1); - m_add_int(p_lka, v); - m_close(p_lka); - - m_create(p_pony, msg, 0, 0, -1); - m_add_int(p_pony, v); - m_close(p_pony); - - m_create(p_queue, msg, 0, 0, -1); - m_add_int(p_queue, v); - m_close(p_queue); - - m_create(p_ca, msg, 0, 0, -1); - m_add_int(p_ca, v); - m_close(p_ca); - - m_create(p_scheduler, msg, 0, 0, -1); - m_add_int(p_scheduler, v); - m_close(p_scheduler); - - m_create(p_parent, msg, 0, 0, -1); - m_add_int(p_parent, v); - m_close(p_parent); -} diff --git a/smtpd/crypto.c b/smtpd/crypto.c deleted file mode 100644 index 20a422cd..00000000 --- a/smtpd/crypto.c +++ /dev/null @@ -1,400 +0,0 @@ -/* $OpenBSD: crypto.c,v 1.8 2019/06/28 13:32:50 deraadt Exp $ */ - -/* - * Copyright (c) 2013 Gilles Chehade <gilles@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/stat.h> - -#include <stdlib.h> -#include <string.h> - -#include <openssl/evp.h> - - -#define CRYPTO_BUFFER_SIZE 16384 - -#define GCM_TAG_SIZE 16 -#define IV_SIZE 12 -#define KEY_SIZE 32 - -/* bump if we ever switch from aes-256-gcm to anything else */ -#define API_VERSION 1 - - -int crypto_setup(const char *, size_t); -int crypto_encrypt_file(FILE *, FILE *); -int crypto_decrypt_file(FILE *, FILE *); -size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t); -size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t); - -static struct crypto_ctx { - unsigned char key[KEY_SIZE]; -} cp; - -int -crypto_setup(const char *key, size_t len) -{ - if (len != KEY_SIZE) - return 0; - - memset(&cp, 0, sizeof cp); - - /* openssl rand -hex 16 */ - memcpy(cp.key, key, sizeof cp.key); - - return 1; -} - -int -crypto_encrypt_file(FILE * in, FILE * out) -{ - EVP_CIPHER_CTX *ctx; - uint8_t ibuf[CRYPTO_BUFFER_SIZE]; - uint8_t obuf[CRYPTO_BUFFER_SIZE]; - uint8_t iv[IV_SIZE]; - uint8_t tag[GCM_TAG_SIZE]; - uint8_t version = API_VERSION; - size_t r, w; - int len; - int ret = 0; - struct stat sb; - - /* XXX - Do NOT encrypt files bigger than 64GB */ - if (fstat(fileno(in), &sb) == -1) - return 0; - if (sb.st_size >= 0x1000000000LL) - return 0; - - /* prepend version byte*/ - if ((w = fwrite(&version, 1, sizeof version, out)) != sizeof version) - return 0; - - /* generate and prepend IV */ - memset(iv, 0, sizeof iv); - arc4random_buf(iv, sizeof iv); - if ((w = fwrite(iv, 1, sizeof iv, out)) != sizeof iv) - return 0; - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return 0; - - EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv); - - /* encrypt until end of file */ - while ((r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in)) != 0) { - if (!EVP_EncryptUpdate(ctx, obuf, &len, ibuf, r)) - goto end; - if (len && (w = fwrite(obuf, len, 1, out)) != 1) - goto end; - } - if (!feof(in)) - goto end; - - /* finalize and write last chunk if any */ - if (!EVP_EncryptFinal_ex(ctx, obuf, &len)) - goto end; - if (len && (w = fwrite(obuf, len, 1, out)) != 1) - goto end; - - /* get and append tag */ - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag); - if ((w = fwrite(tag, sizeof tag, 1, out)) != 1) - goto end; - - fflush(out); - ret = 1; - -end: - EVP_CIPHER_CTX_free(ctx); - return ret; -} - -int -crypto_decrypt_file(FILE * in, FILE * out) -{ - EVP_CIPHER_CTX *ctx; - uint8_t ibuf[CRYPTO_BUFFER_SIZE]; - uint8_t obuf[CRYPTO_BUFFER_SIZE]; - uint8_t iv[IV_SIZE]; - uint8_t tag[GCM_TAG_SIZE]; - uint8_t version; - size_t r, w; - off_t sz; - int len; - int ret = 0; - struct stat sb; - - /* input file too small to be an encrypted file */ - if (fstat(fileno(in), &sb) == -1) - return 0; - if (sb.st_size <= (off_t) (sizeof version + sizeof tag + sizeof iv)) - return 0; - sz = sb.st_size; - - /* extract tag */ - if (fseek(in, -sizeof(tag), SEEK_END) == -1) - return 0; - if ((r = fread(tag, 1, sizeof tag, in)) != sizeof tag) - return 0; - - if (fseek(in, 0, SEEK_SET) == -1) - return 0; - - /* extract version */ - if ((r = fread(&version, 1, sizeof version, in)) != sizeof version) - return 0; - if (version != API_VERSION) - return 0; - - /* extract IV */ - memset(iv, 0, sizeof iv); - if ((r = fread(iv, 1, sizeof iv, in)) != sizeof iv) - return 0; - - /* real ciphertext length */ - sz -= sizeof version; - sz -= sizeof iv; - sz -= sizeof tag; - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return 0; - - EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv); - - /* set expected tag */ - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag); - - /* decrypt until end of ciphertext */ - while (sz) { - if (sz > CRYPTO_BUFFER_SIZE) - r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in); - else - r = fread(ibuf, 1, sz, in); - if (!r) - break; - if (!EVP_DecryptUpdate(ctx, obuf, &len, ibuf, r)) - goto end; - if (len && (w = fwrite(obuf, len, 1, out)) != 1) - goto end; - sz -= r; - } - if (ferror(in)) - goto end; - - /* finalize, write last chunk if any and perform authentication check */ - if (!EVP_DecryptFinal_ex(ctx, obuf, &len)) - goto end; - if (len && (w = fwrite(obuf, len, 1, out)) != 1) - goto end; - - fflush(out); - ret = 1; - -end: - EVP_CIPHER_CTX_free(ctx); - return ret; -} - -size_t -crypto_encrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen) -{ - EVP_CIPHER_CTX *ctx; - uint8_t iv[IV_SIZE]; - uint8_t tag[GCM_TAG_SIZE]; - uint8_t version = API_VERSION; - off_t sz; - int olen; - int len = 0; - int ret = 0; - - /* output buffer does not have enough room */ - if (outlen < inlen + sizeof version + sizeof tag + sizeof iv) - return 0; - - /* input should not exceed 64GB */ - sz = inlen; - if (sz >= 0x1000000000LL) - return 0; - - /* prepend version */ - *out = version; - len++; - - /* generate IV */ - memset(iv, 0, sizeof iv); - arc4random_buf(iv, sizeof iv); - memcpy(out + len, iv, sizeof iv); - len += sizeof iv; - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return 0; - - EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv); - - /* encrypt buffer */ - if (!EVP_EncryptUpdate(ctx, out + len, &olen, in, inlen)) - goto end; - len += olen; - - /* finalize and write last chunk if any */ - if (!EVP_EncryptFinal_ex(ctx, out + len, &olen)) - goto end; - len += olen; - - /* get and append tag */ - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag); - memcpy(out + len, tag, sizeof tag); - ret = len + sizeof tag; - -end: - EVP_CIPHER_CTX_free(ctx); - return ret; -} - -size_t -crypto_decrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen) -{ - EVP_CIPHER_CTX *ctx; - uint8_t iv[IV_SIZE]; - uint8_t tag[GCM_TAG_SIZE]; - int olen; - int len = 0; - int ret = 0; - - /* out does not have enough room */ - if (outlen < inlen - sizeof tag + sizeof iv) - return 0; - - /* extract tag */ - memcpy(tag, in + inlen - sizeof tag, sizeof tag); - inlen -= sizeof tag; - - /* check version */ - if (*in != API_VERSION) - return 0; - in++; - inlen--; - - /* extract IV */ - memset(iv, 0, sizeof iv); - memcpy(iv, in, sizeof iv); - inlen -= sizeof iv; - in += sizeof iv; - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) - return 0; - - EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv); - - /* set expected tag */ - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag); - - /* decrypt buffer */ - if (!EVP_DecryptUpdate(ctx, out, &olen, in, inlen)) - goto end; - len += olen; - - /* finalize, write last chunk if any and perform authentication check */ - if (!EVP_DecryptFinal_ex(ctx, out + len, &olen)) - goto end; - ret = len + olen; - -end: - EVP_CIPHER_CTX_free(ctx); - return ret; -} - -#if 0 -int -main(int argc, char *argv[]) -{ - if (argc != 3) { - printf("usage: crypto <key> <buffer>\n"); - return 1; - } - - if (!crypto_setup(argv[1], strlen(argv[1]))) { - printf("crypto_setup failed\n"); - return 1; - } - - { - char encbuffer[4096]; - size_t enclen; - char decbuffer[4096]; - size_t declen; - - printf("encrypt/decrypt buffer: "); - enclen = crypto_encrypt_buffer(argv[2], strlen(argv[2]), - encbuffer, sizeof encbuffer); - - /* uncomment below to provoke integrity check failure */ - /* - * encbuffer[13] = 0x42; - * encbuffer[14] = 0x42; - * encbuffer[15] = 0x42; - * encbuffer[16] = 0x42; - */ - - declen = crypto_decrypt_buffer(encbuffer, enclen, - decbuffer, sizeof decbuffer); - if (declen != 0 && !strncmp(argv[2], decbuffer, declen)) - printf("ok\n"); - else - printf("nope\n"); - } - - { - FILE *fpin; - FILE *fpout; - printf("encrypt/decrypt file: "); - - fpin = fopen("/etc/passwd", "r"); - fpout = fopen("/tmp/passwd.enc", "w"); - if (!crypto_encrypt_file(fpin, fpout)) { - printf("encryption failed\n"); - return 1; - } - fclose(fpin); - fclose(fpout); - - /* uncomment below to provoke integrity check failure */ - /* - * fpin = fopen("/tmp/passwd.enc", "a"); - * fprintf(fpin, "borken"); - * fclose(fpin); - */ - fpin = fopen("/tmp/passwd.enc", "r"); - fpout = fopen("/tmp/passwd.dec", "w"); - if (!crypto_decrypt_file(fpin, fpout)) - printf("nope\n"); - else - printf("ok\n"); - fclose(fpin); - fclose(fpout); - } - - - return 0; -} -#endif diff --git a/smtpd/dict.c b/smtpd/dict.c deleted file mode 100644 index e660f0a5..00000000 --- a/smtpd/dict.c +++ /dev/null @@ -1,269 +0,0 @@ -/* $OpenBSD: dict.c,v 1.6 2018/12/23 16:06:24 gilles Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/tree.h> - -#include <err.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#include "dict.h" - -struct dictentry { - SPLAY_ENTRY(dictentry) entry; - const char *key; - void *data; -}; - -static int dictentry_cmp(struct dictentry *, struct dictentry *); - -SPLAY_PROTOTYPE(_dict, dictentry, entry, dictentry_cmp); - -int -dict_check(struct dict *d, const char *k) -{ - struct dictentry key; - - key.key = k; - return (SPLAY_FIND(_dict, &d->dict, &key) != NULL); -} - -static inline struct dictentry * -dict_alloc(const char *k, void *data) -{ - struct dictentry *e; - size_t s = strlen(k) + 1; - void *t; - - if ((e = malloc(sizeof(*e) + s)) == NULL) - return NULL; - - e->key = t = (char*)(e) + sizeof(*e); - e->data = data; - memmove(t, k, s); - - return (e); -} - -void * -dict_set(struct dict *d, const char *k, void *data) -{ - struct dictentry *entry, key; - char *old; - - key.key = k; - if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) { - if ((entry = dict_alloc(k, data)) == NULL) - err(1, "dict_set: malloc"); - SPLAY_INSERT(_dict, &d->dict, entry); - old = NULL; - d->count += 1; - } else { - old = entry->data; - entry->data = data; - } - - return (old); -} - -void -dict_xset(struct dict *d, const char * k, void *data) -{ - struct dictentry *entry; - - if ((entry = dict_alloc(k, data)) == NULL) - err(1, "dict_xset: malloc"); - if (SPLAY_INSERT(_dict, &d->dict, entry)) - errx(1, "dict_xset(%p, %s)", d, k); - d->count += 1; -} - -void * -dict_get(struct dict *d, const char *k) -{ - struct dictentry key, *entry; - - key.key = k; - if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) - return (NULL); - - return (entry->data); -} - -void * -dict_xget(struct dict *d, const char *k) -{ - struct dictentry key, *entry; - - key.key = k; - if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) - errx(1, "dict_xget(%p, %s)", d, k); - - return (entry->data); -} - -void * -dict_pop(struct dict *d, const char *k) -{ - struct dictentry key, *entry; - void *data; - - key.key = k; - if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) - return (NULL); - - data = entry->data; - SPLAY_REMOVE(_dict, &d->dict, entry); - free(entry); - d->count -= 1; - - return (data); -} - -void * -dict_xpop(struct dict *d, const char *k) -{ - struct dictentry key, *entry; - void *data; - - key.key = k; - if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) - errx(1, "dict_xpop(%p, %s)", d, k); - - data = entry->data; - SPLAY_REMOVE(_dict, &d->dict, entry); - free(entry); - d->count -= 1; - - return (data); -} - -int -dict_poproot(struct dict *d, void **data) -{ - struct dictentry *entry; - - entry = SPLAY_ROOT(&d->dict); - if (entry == NULL) - return (0); - if (data) - *data = entry->data; - SPLAY_REMOVE(_dict, &d->dict, entry); - free(entry); - d->count -= 1; - - return (1); -} - -int -dict_root(struct dict *d, const char **k, void **data) -{ - struct dictentry *entry; - - entry = SPLAY_ROOT(&d->dict); - if (entry == NULL) - return (0); - if (k) - *k = entry->key; - if (data) - *data = entry->data; - return (1); -} - -int -dict_iter(struct dict *d, void **hdl, const char **k, void **data) -{ - struct dictentry *curr = *hdl; - - if (curr == NULL) - curr = SPLAY_MIN(_dict, &d->dict); - else - curr = SPLAY_NEXT(_dict, &d->dict, curr); - - if (curr) { - *hdl = curr; - if (k) - *k = curr->key; - if (data) - *data = curr->data; - return (1); - } - - return (0); -} - -int -dict_iterfrom(struct dict *d, void **hdl, const char *kfrom, const char **k, - void **data) -{ - struct dictentry *curr = *hdl, key; - - if (curr == NULL) { - if (kfrom == NULL) - curr = SPLAY_MIN(_dict, &d->dict); - else { - key.key = kfrom; - curr = SPLAY_FIND(_dict, &d->dict, &key); - if (curr == NULL) { - SPLAY_INSERT(_dict, &d->dict, &key); - curr = SPLAY_NEXT(_dict, &d->dict, &key); - SPLAY_REMOVE(_dict, &d->dict, &key); - } - } - } else - curr = SPLAY_NEXT(_dict, &d->dict, curr); - - if (curr) { - *hdl = curr; - if (k) - *k = curr->key; - if (data) - *data = curr->data; - return (1); - } - - return (0); -} - -void -dict_merge(struct dict *dst, struct dict *src) -{ - struct dictentry *entry; - - while (!SPLAY_EMPTY(&src->dict)) { - entry = SPLAY_ROOT(&src->dict); - SPLAY_REMOVE(_dict, &src->dict, entry); - if (SPLAY_INSERT(_dict, &dst->dict, entry)) - errx(1, "dict_merge: duplicate"); - } - dst->count += src->count; - src->count = 0; -} - -static int -dictentry_cmp(struct dictentry *a, struct dictentry *b) -{ - return strcmp(a->key, b->key); -} - -SPLAY_GENERATE(_dict, dictentry, entry, dictentry_cmp); diff --git a/smtpd/dict.h b/smtpd/dict.h deleted file mode 100644 index c5d47e1a..00000000 --- a/smtpd/dict.h +++ /dev/null @@ -1,48 +0,0 @@ -/* $OpenBSD: dict.h,v 1.1 2018/12/23 16:06:24 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _DICT_H_ -#define _DICT_H_ - -SPLAY_HEAD(_dict, dictentry); - -struct dict { - struct _dict dict; - size_t count; -}; - - -/* dict.c */ -#define dict_init(d) do { SPLAY_INIT(&((d)->dict)); (d)->count = 0; } while(0) -#define dict_empty(d) SPLAY_EMPTY(&((d)->dict)) -#define dict_count(d) ((d)->count) -int dict_check(struct dict *, const char *); -void *dict_set(struct dict *, const char *, void *); -void dict_xset(struct dict *, const char *, void *); -void *dict_get(struct dict *, const char *); -void *dict_xget(struct dict *, const char *); -void *dict_pop(struct dict *, const char *); -void *dict_xpop(struct dict *, const char *); -int dict_poproot(struct dict *, void **); -int dict_root(struct dict *, const char **, void **); -int dict_iter(struct dict *, void **, const char **, void **); -int dict_iterfrom(struct dict *, void **, const char *, const char **, void **); -void dict_merge(struct dict *, struct dict *); - -#endif diff --git a/smtpd/dns.c b/smtpd/dns.c deleted file mode 100644 index a3107e89..00000000 --- a/smtpd/dns.c +++ /dev/null @@ -1,379 +0,0 @@ -/* $OpenBSD: dns.c,v 1.89 2019/09/18 11:26:30 eric Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/queue.h> -#include <sys/uio.h> - -#include <netinet/in.h> -#include <arpa/inet.h> -#include <arpa/nameser.h> -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include <arpa/nameser_compat.h> -#endif -#include <netdb.h> - -#include <asr.h> -#include <event.h> -#include <netdb.h> -#include <resolv.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" -#include "unpack_dns.h" - -/* On OpenBSD, this function is not needed because we don't free addrinfo */ -#if defined(NOOP_ASR_FREEADDRINFO) -#define asr_freeaddrinfo(x) do { } while(0); -#endif - -struct dns_lookup { - struct dns_session *session; - char *host; - int preference; -}; - -struct dns_session { - struct mproc *p; - uint64_t reqid; - int type; - char name[HOST_NAME_MAX+1]; - size_t mxfound; - int error; - int refcount; -}; - -static void dns_lookup_host(struct dns_session *, const char *, int); -static void dns_dispatch_host(struct asr_result *, void *); -static void dns_dispatch_mx(struct asr_result *, void *); -static void dns_dispatch_mx_preference(struct asr_result *, void *); - -static int -domainname_is_addr(const char *s, struct sockaddr *sa, socklen_t *sl) -{ - struct addrinfo hints, *res; - socklen_t sl2; - size_t l; - char buf[SMTPD_MAXDOMAINPARTSIZE]; - int i6, error; - - if (*s != '[') - return (0); - - i6 = (strncasecmp("[IPv6:", s, 6) == 0); - s += i6 ? 6 : 1; - - l = strlcpy(buf, s, sizeof(buf)); - if (l >= sizeof(buf) || l == 0 || buf[l - 1] != ']') - return (0); - - buf[l - 1] = '\0'; - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_NUMERICHOST; - hints.ai_socktype = SOCK_STREAM; - if (i6) - hints.ai_family = AF_INET6; - - res = NULL; - if ((error = getaddrinfo(buf, NULL, &hints, &res))) { - log_warnx("getaddrinfo: %s", gai_strerror(error)); - } - - if (!res) - return (0); - - if (sa && sl) { - sl2 = *sl; - if (sl2 > res->ai_addrlen) - sl2 = res->ai_addrlen; - memmove(sa, res->ai_addr, sl2); - *sl = res->ai_addrlen; - } - - freeaddrinfo(res); - return (1); -} - -void -dns_imsg(struct mproc *p, struct imsg *imsg) -{ - struct sockaddr_storage ss; - struct dns_session *s; - struct sockaddr *sa; - struct asr_query *as; - struct msg m; - const char *domain, *mx, *host; - socklen_t sl; - - s = xcalloc(1, sizeof *s); - s->type = imsg->hdr.type; - s->p = p; - - m_msg(&m, imsg); - m_get_id(&m, &s->reqid); - - switch (s->type) { - - case IMSG_MTA_DNS_HOST: - m_get_string(&m, &host); - m_end(&m); - dns_lookup_host(s, host, -1); - return; - - case IMSG_MTA_DNS_MX: - m_get_string(&m, &domain); - m_end(&m); - (void)strlcpy(s->name, domain, sizeof(s->name)); - - sa = (struct sockaddr *)&ss; - sl = sizeof(ss); - - if (domainname_is_addr(domain, sa, &sl)) { - m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_string(s->p, sockaddr_to_text(sa)); - m_add_sockaddr(s->p, sa); - m_add_int(s->p, -1); - m_close(s->p); - - m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_int(s->p, DNS_OK); - m_close(s->p); - free(s); - return; - } - - as = res_query_async(s->name, C_IN, T_MX, NULL); - if (as == NULL) { - log_warn("warn: res_query_async: %s", s->name); - m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_int(s->p, DNS_EINVAL); - m_close(s->p); - free(s); - return; - } - - event_asr_run(as, dns_dispatch_mx, s); - return; - - case IMSG_MTA_DNS_MX_PREFERENCE: - m_get_string(&m, &domain); - m_get_string(&m, &mx); - m_end(&m); - (void)strlcpy(s->name, mx, sizeof(s->name)); - - as = res_query_async(domain, C_IN, T_MX, NULL); - if (as == NULL) { - m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_int(s->p, DNS_ENOTFOUND); - m_close(s->p); - free(s); - return; - } - - event_asr_run(as, dns_dispatch_mx_preference, s); - return; - - default: - log_warnx("warn: bad dns request %d", s->type); - fatal(NULL); - } -} - -static void -dns_dispatch_host(struct asr_result *ar, void *arg) -{ - struct dns_session *s; - struct dns_lookup *lookup = arg; - struct addrinfo *ai; - - s = lookup->session; - - for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) { - s->mxfound++; - m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_string(s->p, lookup->host); - m_add_sockaddr(s->p, ai->ai_addr); - m_add_int(s->p, lookup->preference); - m_close(s->p); - } - free(lookup->host); - free(lookup); - if (ar->ar_addrinfo) - asr_freeaddrinfo(ar->ar_addrinfo); - - if (ar->ar_gai_errno) - s->error = ar->ar_gai_errno; - - if (--s->refcount) - return; - - m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_int(s->p, s->mxfound ? DNS_OK : DNS_ENOTFOUND); - m_close(s->p); - free(s); -} - -static void -dns_dispatch_mx(struct asr_result *ar, void *arg) -{ - struct dns_session *s = arg; - struct unpack pack; - struct dns_header h; - struct dns_query q; - struct dns_rr rr; - char buf[512]; - size_t found; - - if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA && - ar->ar_h_errno != NOTIMP) { - - m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); - m_add_id(s->p, s->reqid); - if (ar->ar_rcode == NXDOMAIN) - m_add_int(s->p, DNS_ENONAME); - else if (ar->ar_h_errno == NO_RECOVERY) - m_add_int(s->p, DNS_EINVAL); - else - m_add_int(s->p, DNS_RETRY); - m_close(s->p); - free(s); - free(ar->ar_data); - return; - } - - unpack_init(&pack, ar->ar_data, ar->ar_datalen); - unpack_header(&pack, &h); - unpack_query(&pack, &q); - - found = 0; - for (; h.ancount; h.ancount--) { - unpack_rr(&pack, &rr); - if (rr.rr_type != T_MX) - continue; - print_dname(rr.rr.mx.exchange, buf, sizeof(buf)); - buf[strlen(buf) - 1] = '\0'; - dns_lookup_host(s, buf, rr.rr.mx.preference); - found++; - } - free(ar->ar_data); - - /* fallback to host if no MX is found. */ - if (found == 0) - dns_lookup_host(s, s->name, 0); -} - -static void -dns_dispatch_mx_preference(struct asr_result *ar, void *arg) -{ - struct dns_session *s = arg; - struct unpack pack; - struct dns_header h; - struct dns_query q; - struct dns_rr rr; - char buf[512]; - int error; - - if (ar->ar_h_errno) { - if (ar->ar_rcode == NXDOMAIN) - error = DNS_ENONAME; - else if (ar->ar_h_errno == NO_RECOVERY - || ar->ar_h_errno == NO_DATA) - error = DNS_EINVAL; - else - error = DNS_RETRY; - } - else { - error = DNS_ENOTFOUND; - unpack_init(&pack, ar->ar_data, ar->ar_datalen); - unpack_header(&pack, &h); - unpack_query(&pack, &q); - for (; h.ancount; h.ancount--) { - unpack_rr(&pack, &rr); - if (rr.rr_type != T_MX) - continue; - print_dname(rr.rr.mx.exchange, buf, sizeof(buf)); - buf[strlen(buf) - 1] = '\0'; - if (!strcasecmp(s->name, buf)) { - error = DNS_OK; - break; - } - } - } - - free(ar->ar_data); - - m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1); - m_add_id(s->p, s->reqid); - m_add_int(s->p, error); - if (error == DNS_OK) - m_add_int(s->p, rr.rr.mx.preference); - m_close(s->p); - free(s); -} - -static void -dns_lookup_host(struct dns_session *s, const char *host, int preference) -{ - struct dns_lookup *lookup; - struct addrinfo hints; - char hostcopy[HOST_NAME_MAX+1]; - char *p; - void *as; - - lookup = xcalloc(1, sizeof *lookup); - lookup->preference = preference; - lookup->host = xstrdup(host); - lookup->session = s; - s->refcount++; - - if (*host == '[') { - if (strncasecmp("[IPv6:", host, 6) == 0) - host += 6; - else - host += 1; - (void)strlcpy(hostcopy, host, sizeof hostcopy); - p = strchr(hostcopy, ']'); - if (p) - *p = 0; - host = hostcopy; - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG; - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - as = getaddrinfo_async(host, NULL, &hints, NULL); - event_asr_run(as, dns_dispatch_host, lookup); -} diff --git a/smtpd/enqueue.c b/smtpd/enqueue.c deleted file mode 100644 index 0ef694b5..00000000 --- a/smtpd/enqueue.c +++ /dev/null @@ -1,932 +0,0 @@ -/* $OpenBSD: enqueue.c,v 1.118 2020/03/18 20:17:14 eric Exp $ */ - -/* - * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <grp.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" - -extern struct imsgbuf *ibuf; - -void usage(void); -static void build_from(char *, struct passwd *); -static int parse_message(FILE *, int, int, FILE *); -static void parse_addr(char *, size_t, int); -static void parse_addr_terminal(int); -static char *qualify_addr(char *); -static void rcpt_add(char *); -static int open_connection(void); -static int get_responses(FILE *, int); -static int send_line(FILE *, int, char *, ...); -static int enqueue_offline(int, char *[], FILE *, FILE *); -static int savedeadletter(struct passwd *, FILE *); - -extern int srv_connected(void); - -enum headerfields { - HDR_NONE, - HDR_FROM, - HDR_TO, - HDR_CC, - HDR_BCC, - HDR_SUBJECT, - HDR_DATE, - HDR_MSGID, - HDR_MIME_VERSION, - HDR_CONTENT_TYPE, - HDR_CONTENT_DISPOSITION, - HDR_CONTENT_TRANSFER_ENCODING, - HDR_USER_AGENT -}; - -struct { - char *word; - enum headerfields type; -} keywords[] = { - { "From:", HDR_FROM }, - { "To:", HDR_TO }, - { "Cc:", HDR_CC }, - { "Bcc:", HDR_BCC }, - { "Subject:", HDR_SUBJECT }, - { "Date:", HDR_DATE }, - { "Message-Id:", HDR_MSGID }, - { "MIME-Version:", HDR_MIME_VERSION }, - { "Content-Type:", HDR_CONTENT_TYPE }, - { "Content-Disposition:", HDR_CONTENT_DISPOSITION }, - { "Content-Transfer-Encoding:", HDR_CONTENT_TRANSFER_ENCODING }, - { "User-Agent:", HDR_USER_AGENT }, -}; - -#define LINESPLIT 990 -#define SMTP_LINELEN 1000 -#define TIMEOUTMSG "Timeout\n" - -#define WSP(c) (c == ' ' || c == '\t') - -int verbose = 0; -static char host[HOST_NAME_MAX+1]; -char *user = NULL; -time_t timestamp; - -struct { - int fd; - char *from; - char *fromname; - char **rcpts; - char *dsn_notify; - char *dsn_ret; - char *dsn_envid; - int rcpt_cnt; - int need_linesplit; - int saw_date; - int saw_msgid; - int saw_from; - int saw_mime_version; - int saw_content_type; - int saw_content_disposition; - int saw_content_transfer_encoding; - int saw_user_agent; - int noheader; -} msg; - -struct { - uint quote; - uint comment; - uint esc; - uint brackets; - size_t wpos; - char buf[SMTP_LINELEN]; -} pstate; - -#define QP_TEST_WRAP(fp, buf, linelen, size) do { \ - if (((linelen) += (size)) + 1 > 76) { \ - fprintf((fp), "=\r\n"); \ - if (buf[0] == '.') \ - fprintf((fp), "."); \ - (linelen) = (size); \ - } \ -} while (0) - -/* RFC 2045 section 6.7 */ -static void -qp_encoded_write(FILE *fp, char *buf) -{ - size_t linelen = 0; - - for (;buf[0] != '\0' && buf[0] != '\n'; buf++) { - /* - * Point 3: Any TAB (HT) or SPACE characters on an encoded line - * MUST thus be followed on that line by a printable character. - * - * Ergo, only encode if the next character is EOL. - */ - if (buf[0] == ' ' || buf[0] == '\t') { - if (buf[1] == '\n') { - QP_TEST_WRAP(fp, buf, linelen, 3); - fprintf(fp, "=%2X", *buf & 0xff); - } else { - QP_TEST_WRAP(fp, buf, linelen, 1); - fprintf(fp, "%c", *buf & 0xff); - } - /* - * Point 1, with exclusion of point 2, skip EBCDIC NOTE. - * Do this after whitespace check, else they would match here. - */ - } else if (!((buf[0] >= 33 && buf[0] <= 60) || - (buf[0] >= 62 && buf[0] <= 126))) { - QP_TEST_WRAP(fp, buf, linelen, 3); - fprintf(fp, "=%2X", *buf & 0xff); - /* Point 2: 33 through 60 inclusive, and 62 through 126 */ - } else { - QP_TEST_WRAP(fp, buf, linelen, 1); - fprintf(fp, "%c", *buf); - } - } - fprintf(fp, "\r\n"); -} - -int -enqueue(int argc, char *argv[], FILE *ofp) -{ - int i, ch, tflag = 0; - char *fake_from = NULL, *buf = NULL; - struct passwd *pw; - FILE *fp = NULL, *fout; - size_t sz = 0, envid_sz = 0; - ssize_t len; - char *line; - int inheaders = 1; - int save_argc; - char **save_argv; - int no_getlogin = 0; - - memset(&msg, 0, sizeof(msg)); - time(×tamp); - - save_argc = argc; - save_argv = argv; - - while ((ch = getopt(argc, argv, - "A:B:b:E::e:F:f:iJ::L:mN:o:p:qr:R:StvV:x")) != -1) { - switch (ch) { - case 'f': - fake_from = optarg; - break; - case 'F': - msg.fromname = optarg; - break; - case 'N': - msg.dsn_notify = optarg; - break; - case 'r': - fake_from = optarg; - break; - case 'R': - msg.dsn_ret = optarg; - break; - case 'S': - no_getlogin = 1; - break; - case 't': - tflag = 1; - break; - case 'v': - verbose = 1; - break; - case 'V': - msg.dsn_envid = optarg; - break; - /* all remaining: ignored, sendmail compat */ - case 'A': - case 'B': - case 'b': - case 'E': - case 'e': - case 'i': - case 'L': - case 'm': - case 'o': - case 'p': - case 'x': - break; - case 'q': - /* XXX: implement "process all now" */ - return (EX_SOFTWARE); - default: - usage(); - } - } - - argc -= optind; - argv += optind; - - if (getmailname(host, sizeof(host)) == -1) - errx(EX_NOHOST, "getmailname"); - if (no_getlogin) { - if ((pw = getpwuid(getuid())) == NULL) - user = "anonymous"; - if (pw != NULL) - user = xstrdup(pw->pw_name); - } - else { - uid_t ruid = getuid(); - - if ((user = getlogin()) != NULL && *user != '\0') { - if ((pw = getpwnam(user)) == NULL || - (ruid != 0 && ruid != pw->pw_uid)) - pw = getpwuid(ruid); - } else if ((pw = getpwuid(ruid)) == NULL) { - user = "anonymous"; - } - user = xstrdup(pw ? pw->pw_name : user); - } - - build_from(fake_from, pw); - - while (argc > 0) { - rcpt_add(argv[0]); - argv++; - argc--; - } - - if ((fp = tmpfile()) == NULL) - err(EX_UNAVAILABLE, "tmpfile"); - - msg.noheader = parse_message(stdin, fake_from == NULL, tflag, fp); - - if (msg.rcpt_cnt == 0) - errx(EX_SOFTWARE, "no recipients"); - - /* init session */ - rewind(fp); - - /* check if working in offline mode */ - /* If the server is not running, enqueue the message offline */ - - if (!srv_connected()) { -#if HAVE_PLEDGE - if (pledge("stdio", NULL) == -1) - err(1, "pledge"); -#endif - return (enqueue_offline(save_argc, save_argv, fp, ofp)); - } - - if ((msg.fd = open_connection()) == -1) - errx(EX_UNAVAILABLE, "server too busy"); - -#if HAVE_PLEDGE - if (pledge("stdio wpath cpath", NULL) == -1) - err(1, "pledge"); -#endif - - fout = fdopen(msg.fd, "a+"); - if (fout == NULL) - err(EX_UNAVAILABLE, "fdopen"); - - /* - * We need to call get_responses after every command because we don't - * support PIPELINING on the server-side yet. - */ - - /* banner */ - if (!get_responses(fout, 1)) - goto fail; - - if (!send_line(fout, verbose, "EHLO localhost\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - if (msg.dsn_envid != NULL) - envid_sz = strlen(msg.dsn_envid); - - if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\r\n", - msg.from, - msg.dsn_ret ? "RET=" : "", - msg.dsn_ret ? msg.dsn_ret : "", - envid_sz ? "ENVID=" : "", - envid_sz ? msg.dsn_envid : "")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - for (i = 0; i < msg.rcpt_cnt; i++) { - if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\r\n", - msg.rcpts[i], - msg.dsn_notify ? "NOTIFY=" : "", - msg.dsn_notify ? msg.dsn_notify : "")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - } - - if (!send_line(fout, verbose, "DATA\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - /* add From */ - if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\r\n", - msg.fromname ? msg.fromname : "", msg.fromname ? " " : "", - msg.from)) - goto fail; - - /* add Date */ - if (!msg.saw_date && !send_line(fout, 0, "Date: %s\r\n", - time_to_text(timestamp))) - goto fail; - - if (msg.need_linesplit) { - /* we will always need to mime encode for long lines */ - if (!msg.saw_mime_version && !send_line(fout, 0, - "MIME-Version: 1.0\r\n")) - goto fail; - if (!msg.saw_content_type && !send_line(fout, 0, - "Content-Type: text/plain; charset=unknown-8bit\r\n")) - goto fail; - if (!msg.saw_content_disposition && !send_line(fout, 0, - "Content-Disposition: inline\r\n")) - goto fail; - if (!msg.saw_content_transfer_encoding && !send_line(fout, 0, - "Content-Transfer-Encoding: quoted-printable\r\n")) - goto fail; - } - - /* add separating newline */ - if (msg.noheader) { - if (!send_line(fout, 0, "\r\n")) - goto fail; - inheaders = 0; - } - - for (;;) { - if ((len = getline(&buf, &sz, fp)) == -1) { - if (feof(fp)) - break; - else - err(EX_UNAVAILABLE, "getline"); - } - - /* newlines have been normalized on first parsing */ - if (buf[len-1] != '\n') - errx(EX_SOFTWARE, "expect EOL"); - len--; - - if (buf[0] == '.') { - if (fputc('.', fout) == EOF) - goto fail; - } - - line = buf; - - if (inheaders) { - if (strncasecmp("from ", line, 5) == 0) - continue; - if (strncasecmp("return-path: ", line, 13) == 0) - continue; - } - - if (msg.saw_content_transfer_encoding || msg.noheader || - inheaders || !msg.need_linesplit) { - if (!send_line(fout, 0, "%.*s\r\n", (int)len, line)) - goto fail; - if (inheaders && buf[0] == '\n') - inheaders = 0; - continue; - } - - /* we don't have a content transfer encoding, use our default */ - qp_encoded_write(fout, line); - } - free(buf); - if (!send_line(fout, verbose, ".\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - if (!send_line(fout, verbose, "QUIT\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - fclose(fp); - fclose(fout); - - exit(EX_OK); - -fail: - if (pw) - savedeadletter(pw, fp); - exit(EX_SOFTWARE); -} - -static int -get_responses(FILE *fin, int n) -{ - char *buf = NULL; - size_t sz = 0; - ssize_t len; - int e, ret = 0; - - fflush(fin); - if ((e = ferror(fin))) { - warnx("ferror: %d", e); - goto err; - } - - while (n) { - if ((len = getline(&buf, &sz, fin)) == -1) { - if (ferror(fin)) { - warn("getline"); - goto err; - } else if (feof(fin)) - break; - else - err(EX_UNAVAILABLE, "getline"); - } - - /* account for \r\n linebreaks */ - if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') - buf[--len - 1] = '\n'; - - if (len < 4) { - warnx("bad response"); - goto err; - } - - if (verbose) - printf("<<< %.*s", (int)len, buf); - - if (buf[3] == '-') - continue; - if (buf[0] != '2' && buf[0] != '3') { - warnx("command failed: %.*s", (int)len, buf); - goto err; - } - n--; - } - - ret = 1; -err: - free(buf); - return ret; -} - -static int -send_line(FILE *fp, int v, char *fmt, ...) -{ - int ret = 0; - va_list ap; - - va_start(ap, fmt); - if (vfprintf(fp, fmt, ap) >= 0) - ret = 1; - va_end(ap); - - if (ret && v) { - printf(">>> "); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - } - - return (ret); -} - -static void -build_from(char *fake_from, struct passwd *pw) -{ - char *p; - - if (fake_from == NULL) - msg.from = qualify_addr(user); - else { - if (fake_from[0] == '<') { - if (fake_from[strlen(fake_from) - 1] != '>') - errx(1, "leading < but no trailing >"); - fake_from[strlen(fake_from) - 1] = 0; - p = xstrdup(fake_from + 1); - - msg.from = qualify_addr(p); - free(p); - } else - msg.from = qualify_addr(fake_from); - } - - if (msg.fromname == NULL && fake_from == NULL && pw != NULL) { - int len, apos; - - len = strcspn(pw->pw_gecos, ","); - if ((p = memchr(pw->pw_gecos, '&', len))) { - apos = p - pw->pw_gecos; - if (asprintf(&msg.fromname, "%.*s%s%.*s", - apos, pw->pw_gecos, - pw->pw_name, - len - apos - 1, p + 1) == -1) - err(1, "asprintf"); - msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]); - } else { - if (asprintf(&msg.fromname, "%.*s", len, - pw->pw_gecos) == -1) - err(1, "asprintf"); - } - } -} - -static int -parse_message(FILE *fin, int get_from, int tflag, FILE *fout) -{ - char *buf = NULL; - size_t sz = 0; - ssize_t len; - uint i, cur = HDR_NONE; - uint header_seen = 0, header_done = 0; - - memset(&pstate, 0, sizeof(pstate)); - for (;;) { - if ((len = getline(&buf, &sz, fin)) == -1) { - if (feof(fin)) - break; - else - err(EX_UNAVAILABLE, "getline"); - } - - /* account for \r\n linebreaks */ - if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') - buf[--len - 1] = '\n'; - - if (len == 1 && buf[0] == '\n') /* end of header */ - header_done = 1; - - if (!WSP(buf[0])) { /* whitespace -> continuation */ - if (cur == HDR_FROM) - parse_addr_terminal(1); - if (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC) - parse_addr_terminal(0); - cur = HDR_NONE; - } - - /* not really exact, if we are still in headers */ - if (len + (buf[len - 1] == '\n' ? 0 : 1) >= LINESPLIT) - msg.need_linesplit = 1; - - for (i = 0; !header_done && cur == HDR_NONE && - i < nitems(keywords); i++) - if ((size_t)len > strlen(keywords[i].word) && - !strncasecmp(buf, keywords[i].word, - strlen(keywords[i].word))) - cur = keywords[i].type; - - if (cur != HDR_NONE) - header_seen = 1; - - if (cur != HDR_BCC) { - if (!send_line(fout, 0, "%.*s", (int)len, buf)) - err(1, "write error"); - if (buf[len - 1] != '\n') { - if (fputc('\n', fout) == EOF) - err(1, "write error"); - } - } - - /* - * using From: as envelope sender is not sendmail compatible, - * but I really want it that way - maybe needs a knob - */ - if (cur == HDR_FROM) { - msg.saw_from++; - if (get_from) - parse_addr(buf, len, 1); - } - - if (tflag && (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC)) - parse_addr(buf, len, 0); - - if (cur == HDR_DATE) - msg.saw_date++; - if (cur == HDR_MSGID) - msg.saw_msgid++; - if (cur == HDR_MIME_VERSION) - msg.saw_mime_version = 1; - if (cur == HDR_CONTENT_TYPE) - msg.saw_content_type = 1; - if (cur == HDR_CONTENT_DISPOSITION) - msg.saw_content_disposition = 1; - if (cur == HDR_CONTENT_TRANSFER_ENCODING) - msg.saw_content_transfer_encoding = 1; - if (cur == HDR_USER_AGENT) - msg.saw_user_agent = 1; - } - - free(buf); - return (!header_seen); -} - -static void -parse_addr(char *s, size_t len, int is_from) -{ - size_t pos = 0; - int terminal = 0; - - /* unless this is a continuation... */ - if (!WSP(s[pos]) && s[pos] != ',' && s[pos] != ';') { - /* ... skip over everything before the ':' */ - for (; pos < len && s[pos] != ':'; pos++) - ; /* nothing */ - /* ... and check & reset parser state */ - parse_addr_terminal(is_from); - } - - /* skip over ':' ',' ';' and whitespace */ - for (; pos < len && !pstate.quote && (WSP(s[pos]) || s[pos] == ':' || - s[pos] == ',' || s[pos] == ';'); pos++) - ; /* nothing */ - - for (; pos < len; pos++) { - if (!pstate.esc && !pstate.quote && s[pos] == '(') - pstate.comment++; - if (!pstate.comment && !pstate.esc && s[pos] == '"') - pstate.quote = !pstate.quote; - - if (!pstate.comment && !pstate.quote && !pstate.esc) { - if (s[pos] == ':') { /* group */ - for (pos++; pos < len && WSP(s[pos]); pos++) - ; /* nothing */ - pstate.wpos = 0; - } - if (s[pos] == '\n' || s[pos] == '\r') - break; - if (s[pos] == ',' || s[pos] == ';') { - terminal = 1; - break; - } - if (s[pos] == '<') { - pstate.brackets = 1; - pstate.wpos = 0; - } - if (pstate.brackets && s[pos] == '>') - terminal = 1; - } - - if (!pstate.comment && !terminal && (!(!(pstate.quote || - pstate.esc) && (s[pos] == '<' || WSP(s[pos]))))) { - if (pstate.wpos >= sizeof(pstate.buf)) - errx(1, "address exceeds buffer size"); - pstate.buf[pstate.wpos++] = s[pos]; - } - - if (!pstate.quote && pstate.comment && s[pos] == ')') - pstate.comment--; - - if (!pstate.esc && !pstate.comment && s[pos] == '\\') - pstate.esc = 1; - else - pstate.esc = 0; - } - - if (terminal) - parse_addr_terminal(is_from); - - for (; pos < len && (s[pos] == '\r' || s[pos] == '\n'); pos++) - ; /* nothing */ - - if (pos < len) - parse_addr(s + pos, len - pos, is_from); -} - -static void -parse_addr_terminal(int is_from) -{ - if (pstate.comment || pstate.quote || pstate.esc) - errx(1, "syntax error in address"); - if (pstate.wpos) { - if (pstate.wpos >= sizeof(pstate.buf)) - errx(1, "address exceeds buffer size"); - pstate.buf[pstate.wpos] = '\0'; - if (is_from) - msg.from = qualify_addr(pstate.buf); - else - rcpt_add(pstate.buf); - pstate.wpos = 0; - } -} - -static char * -qualify_addr(char *in) -{ - char *out; - - if (strlen(in) > 0 && strchr(in, '@') == NULL) { - if (asprintf(&out, "%s@%s", in, host) == -1) - err(1, "qualify asprintf"); - } else - out = xstrdup(in); - - return (out); -} - -static void -rcpt_add(char *addr) -{ - void *nrcpts; - char *p; - int n; - - n = 1; - p = addr; - while ((p = strchr(p, ',')) != NULL) { - n++; - p++; - } - - if ((nrcpts = reallocarray(msg.rcpts, - msg.rcpt_cnt + n, sizeof(char *))) == NULL) - err(1, "rcpt_add realloc"); - msg.rcpts = nrcpts; - - while (n--) { - if ((p = strchr(addr, ',')) != NULL) - *p++ = '\0'; - msg.rcpts[msg.rcpt_cnt++] = qualify_addr(addr); - if (p == NULL) - break; - addr = p; - } -} - -static int -open_connection(void) -{ - struct imsg imsg; - int fd; - int n; - - imsg_compose(ibuf, IMSG_CTL_SMTP_SESSION, IMSG_VERSION, 0, -1, NULL, 0); - - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) - err(1, "write error"); - - while (1) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - errx(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); - - if ((n = imsg_get(ibuf, &imsg)) == -1) - errx(1, "imsg_get error"); - if (n == 0) - continue; - - switch (imsg.hdr.type) { - case IMSG_CTL_OK: - break; - case IMSG_CTL_FAIL: - errx(1, "server disallowed submission request"); - default: - errx(1, "unexpected imsg reply type"); - } - - fd = imsg.fd; - imsg_free(&imsg); - - break; - } - - return fd; -} - -static int -enqueue_offline(int argc, char *argv[], FILE *ifile, FILE *ofile) -{ - int i, ch; - - for (i = 1; i < argc; i++) { - if (strchr(argv[i], '|') != NULL) { - warnx("%s contains illegal character", argv[i]); - ftruncate(fileno(ofile), 0); - exit(EX_SOFTWARE); - } - if (fprintf(ofile, "%s%s", i == 1 ? "" : "|", argv[i]) < 0) - goto write_error; - } - - if (fputc('\n', ofile) == EOF) - goto write_error; - - while ((ch = fgetc(ifile)) != EOF) { - if (fputc(ch, ofile) == EOF) - goto write_error; - } - - if (ferror(ifile)) { - warn("read error"); - ftruncate(fileno(ofile), 0); - exit(EX_UNAVAILABLE); - } - - if (fclose(ofile) == EOF) - goto write_error; - - return (EX_TEMPFAIL); -write_error: - warn("write error"); - ftruncate(fileno(ofile), 0); - exit(EX_UNAVAILABLE); -} - -static int -savedeadletter(struct passwd *pw, FILE *in) -{ - char buffer[PATH_MAX]; - FILE *fp; - char *buf = NULL; - size_t sz = 0; - ssize_t len; - - (void)snprintf(buffer, sizeof buffer, "%s/dead.letter", pw->pw_dir); - - if (fseek(in, 0, SEEK_SET) != 0) - return 0; - - if ((fp = fopen(buffer, "w")) == NULL) - return 0; - - /* add From */ - if (!msg.saw_from) - fprintf(fp, "From: %s%s<%s>\n", - msg.fromname ? msg.fromname : "", - msg.fromname ? " " : "", - msg.from); - - /* add Date */ - if (!msg.saw_date) - fprintf(fp, "Date: %s\n", time_to_text(timestamp)); - - if (msg.need_linesplit) { - /* we will always need to mime encode for long lines */ - if (!msg.saw_mime_version) - fprintf(fp, "MIME-Version: 1.0\n"); - if (!msg.saw_content_type) - fprintf(fp, "Content-Type: text/plain; " - "charset=unknown-8bit\n"); - if (!msg.saw_content_disposition) - fprintf(fp, "Content-Disposition: inline\n"); - if (!msg.saw_content_transfer_encoding) - fprintf(fp, "Content-Transfer-Encoding: " - "quoted-printable\n"); - } - - /* add separating newline */ - if (msg.noheader) - fprintf(fp, "\n"); - - while ((len = getline(&buf, &sz, in)) != -1) { - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - fprintf(fp, "%s\n", buf); - } - - free(buf); - fprintf(fp, "\n"); - fclose(fp); - return 1; -} diff --git a/smtpd/envelope.c b/smtpd/envelope.c deleted file mode 100644 index 35d98b79..00000000 --- a/smtpd/envelope.c +++ /dev/null @@ -1,786 +0,0 @@ -/* $OpenBSD: envelope.c,v 1.47 2019/11/25 14:18:32 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2011-2013 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -static int envelope_ascii_load(struct envelope *, struct dict *); -static void envelope_ascii_dump(const struct envelope *, char **, size_t *, - const char *); - -void -envelope_set_errormsg(struct envelope *e, char *fmt, ...) -{ - int ret; - va_list ap; - - va_start(ap, fmt); - ret = vsnprintf(e->errorline, sizeof(e->errorline), fmt, ap); - va_end(ap); - - /* this should not happen */ - if (ret < 0) - err(1, "vsnprintf"); - - if ((size_t)ret >= sizeof(e->errorline)) - (void)strlcpy(e->errorline + (sizeof(e->errorline) - 4), - "...", 4); -} - -void -envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class) -{ - e->esc_class = class; -} - -void -envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code) -{ - e->esc_code = code; -} - -static int -envelope_buffer_to_dict(struct dict *d, const char *ibuf, size_t buflen) -{ - static char lbuf[sizeof(struct envelope)]; - size_t len; - char *buf, *field, *nextline; - - memset(lbuf, 0, sizeof lbuf); - if (strlcpy(lbuf, ibuf, sizeof lbuf) >= sizeof lbuf) - goto err; - buf = lbuf; - - while (buflen > 0) { - len = strcspn(buf, "\n"); - buf[len] = '\0'; - nextline = buf + len + 1; - buflen -= (nextline - buf); - - field = buf; - while (*buf && (isalnum((unsigned char)*buf) || *buf == '-')) - buf++; - if (!*buf) - goto err; - - /* skip whitespaces before separator */ - while (*buf && isspace((unsigned char)*buf)) - *buf++ = 0; - - /* we *want* ':' */ - if (*buf != ':') - goto err; - *buf++ = 0; - - /* skip whitespaces after separator */ - while (*buf && isspace((unsigned char)*buf)) - *buf++ = 0; - dict_set(d, field, buf); - buf = nextline; - } - - return (1); - -err: - return (0); -} - -int -envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen) -{ - struct dict d; - const char *val, *errstr; - long long version; - int ret = 0; - - dict_init(&d); - if (!envelope_buffer_to_dict(&d, ibuf, buflen)) { - log_debug("debug: cannot parse envelope to dict"); - goto end; - } - - val = dict_get(&d, "version"); - if (val == NULL) { - log_debug("debug: envelope version not found"); - goto end; - } - version = strtonum(val, 1, 64, &errstr); - if (errstr) { - log_debug("debug: cannot parse envelope version: %s", val); - goto end; - } - - if (version != SMTPD_ENVELOPE_VERSION) { - log_debug("debug: bad envelope version %lld", version); - goto end; - } - - memset(ep, 0, sizeof *ep); - ret = envelope_ascii_load(ep, &d); - if (ret) - ep->version = SMTPD_ENVELOPE_VERSION; -end: - while (dict_poproot(&d, NULL)) - ; - return (ret); -} - -int -envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) -{ - char *p = dest; - - envelope_ascii_dump(ep, &dest, &len, "version"); - envelope_ascii_dump(ep, &dest, &len, "dispatcher"); - envelope_ascii_dump(ep, &dest, &len, "tag"); - envelope_ascii_dump(ep, &dest, &len, "type"); - envelope_ascii_dump(ep, &dest, &len, "smtpname"); - envelope_ascii_dump(ep, &dest, &len, "helo"); - envelope_ascii_dump(ep, &dest, &len, "hostname"); - envelope_ascii_dump(ep, &dest, &len, "username"); - envelope_ascii_dump(ep, &dest, &len, "errorline"); - envelope_ascii_dump(ep, &dest, &len, "sockaddr"); - envelope_ascii_dump(ep, &dest, &len, "sender"); - envelope_ascii_dump(ep, &dest, &len, "rcpt"); - envelope_ascii_dump(ep, &dest, &len, "dest"); - envelope_ascii_dump(ep, &dest, &len, "ctime"); - envelope_ascii_dump(ep, &dest, &len, "last-try"); - envelope_ascii_dump(ep, &dest, &len, "last-bounce"); - envelope_ascii_dump(ep, &dest, &len, "ttl"); - envelope_ascii_dump(ep, &dest, &len, "retry"); - envelope_ascii_dump(ep, &dest, &len, "flags"); - envelope_ascii_dump(ep, &dest, &len, "dsn-notify"); - envelope_ascii_dump(ep, &dest, &len, "dsn-ret"); - envelope_ascii_dump(ep, &dest, &len, "dsn-envid"); - envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt"); - envelope_ascii_dump(ep, &dest, &len, "esc-class"); - envelope_ascii_dump(ep, &dest, &len, "esc-code"); - - switch (ep->type) { - case D_MDA: - envelope_ascii_dump(ep, &dest, &len, "mda-exec"); - envelope_ascii_dump(ep, &dest, &len, "mda-subaddress"); - envelope_ascii_dump(ep, &dest, &len, "mda-user"); - break; - case D_MTA: - break; - case D_BOUNCE: - envelope_ascii_dump(ep, &dest, &len, "bounce-ttl"); - envelope_ascii_dump(ep, &dest, &len, "bounce-delay"); - envelope_ascii_dump(ep, &dest, &len, "bounce-type"); - break; - default: - return (0); - } - - if (dest == NULL) - return (0); - - return (dest - p); -} - -static int -ascii_load_uint8(uint8_t *dest, char *buf) -{ - const char *errstr; - - *dest = strtonum(buf, 0, 0xff, &errstr); - if (errstr) - return 0; - return 1; -} - -static int -ascii_load_uint16(uint16_t *dest, char *buf) -{ - const char *errstr; - - *dest = strtonum(buf, 0, 0xffff, &errstr); - if (errstr) - return 0; - return 1; -} - -static int -ascii_load_uint32(uint32_t *dest, char *buf) -{ - const char *errstr; - - *dest = strtonum(buf, 0, 0xffffffff, &errstr); - if (errstr) - return 0; - return 1; -} - -static int -ascii_load_time(time_t *dest, char *buf) -{ - const char *errstr; - - *dest = strtonum(buf, 0, LLONG_MAX, &errstr); - if (errstr) - return 0; - return 1; -} - -static int -ascii_load_type(enum delivery_type *dest, char *buf) -{ - if (strcasecmp(buf, "mda") == 0) - *dest = D_MDA; - else if (strcasecmp(buf, "mta") == 0) - *dest = D_MTA; - else if (strcasecmp(buf, "bounce") == 0) - *dest = D_BOUNCE; - else - return 0; - return 1; -} - -static int -ascii_load_string(char *dest, char *buf, size_t len) -{ - if (strlcpy(dest, buf, len) >= len) - return 0; - return 1; -} - -static int -ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf) -{ - struct sockaddr_in6 ssin6; - struct sockaddr_in ssin; - - memset(&ssin, 0, sizeof ssin); - memset(&ssin6, 0, sizeof ssin6); - - if (!strcmp("local", buf)) { - ss->ss_family = AF_LOCAL; - } - else if (strncasecmp("IPv6:", buf, 5) == 0) { - /* XXX - remove this after 6.6 release */ - if (inet_pton(AF_INET6, buf + 5, &ssin6.sin6_addr) != 1) - return 0; - ssin6.sin6_family = AF_INET6; - memcpy(ss, &ssin6, sizeof(ssin6)); -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN - ss->ss_len = sizeof(struct sockaddr_in6); -#endif - } - else if (buf[0] == '[' && buf[strlen(buf)-1] == ']') { - buf[strlen(buf)-1] = '\0'; - if (inet_pton(AF_INET6, buf+1, &ssin6.sin6_addr) != 1) - return 0; - ssin6.sin6_family = AF_INET6; - memcpy(ss, &ssin6, sizeof(ssin6)); -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN - ss->ss_len = sizeof(struct sockaddr_in6); -#endif - } - else { - if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1) - return 0; - ssin.sin_family = AF_INET; - memcpy(ss, &ssin, sizeof(ssin)); -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN - ss->ss_len = sizeof(struct sockaddr_in); -#endif - } - return 1; -} - -static int -ascii_load_mailaddr(struct mailaddr *dest, char *buf) -{ - if (!text_to_mailaddr(dest, buf)) - return 0; - return 1; -} - -static int -ascii_load_flags(enum envelope_flags *dest, char *buf) -{ - char *flag; - - while ((flag = strsep(&buf, " ,|")) != NULL) { - if (strcasecmp(flag, "authenticated") == 0) - *dest |= EF_AUTHENTICATED; - else if (strcasecmp(flag, "enqueued") == 0) - ; - else if (strcasecmp(flag, "bounce") == 0) - *dest |= EF_BOUNCE; - else if (strcasecmp(flag, "internal") == 0) - *dest |= EF_INTERNAL; - else - return 0; - } - return 1; -} - -static int -ascii_load_bounce_type(enum bounce_type *dest, char *buf) -{ - if (strcasecmp(buf, "error") == 0 || strcasecmp(buf, "failed") == 0) - *dest = B_FAILED; - else if (strcasecmp(buf, "warn") == 0 || - strcasecmp(buf, "delayed") == 0) - *dest = B_DELAYED; - else if (strcasecmp(buf, "dsn") == 0 || - strcasecmp(buf, "delivered") == 0) - *dest = B_DELIVERED; - else - return 0; - return 1; -} - -static int -ascii_load_dsn_ret(enum dsn_ret *ret, char *buf) -{ - if (strcasecmp(buf, "HDRS") == 0) - *ret = DSN_RETHDRS; - else if (strcasecmp(buf, "FULL") == 0) - *ret = DSN_RETFULL; - else - return 0; - return 1; -} - -static int -ascii_load_field(const char *field, struct envelope *ep, char *buf) -{ - if (strcasecmp("dispatcher", field) == 0) - return ascii_load_string(ep->dispatcher, buf, - sizeof ep->dispatcher); - - if (strcasecmp("bounce-delay", field) == 0) - return ascii_load_time(&ep->agent.bounce.delay, buf); - - if (strcasecmp("bounce-ttl", field) == 0) - return ascii_load_time(&ep->agent.bounce.ttl, buf); - - if (strcasecmp("bounce-type", field) == 0) - return ascii_load_bounce_type(&ep->agent.bounce.type, buf); - - if (strcasecmp("ctime", field) == 0) - return ascii_load_time(&ep->creation, buf); - - if (strcasecmp("dest", field) == 0) - return ascii_load_mailaddr(&ep->dest, buf); - - if (strcasecmp("username", field) == 0) - return ascii_load_string(ep->username, buf, sizeof(ep->username)); - - if (strcasecmp("errorline", field) == 0) - return ascii_load_string(ep->errorline, buf, - sizeof ep->errorline); - - if (strcasecmp("ttl", field) == 0) - return ascii_load_time(&ep->ttl, buf); - - if (strcasecmp("flags", field) == 0) - return ascii_load_flags(&ep->flags, buf); - - if (strcasecmp("helo", field) == 0) - return ascii_load_string(ep->helo, buf, sizeof ep->helo); - - if (strcasecmp("hostname", field) == 0) - return ascii_load_string(ep->hostname, buf, - sizeof ep->hostname); - - if (strcasecmp("last-bounce", field) == 0) - return ascii_load_time(&ep->lastbounce, buf); - - if (strcasecmp("last-try", field) == 0) - return ascii_load_time(&ep->lasttry, buf); - - if (strcasecmp("retry", field) == 0) - return ascii_load_uint16(&ep->retry, buf); - - if (strcasecmp("rcpt", field) == 0) - return ascii_load_mailaddr(&ep->rcpt, buf); - - if (strcasecmp("mda-exec", field) == 0) - return ascii_load_string(ep->mda_exec, buf, sizeof(ep->mda_exec)); - - if (strcasecmp("mda-subaddress", field) == 0) - return ascii_load_string(ep->mda_subaddress, buf, sizeof(ep->mda_subaddress)); - - if (strcasecmp("mda-user", field) == 0) - return ascii_load_string(ep->mda_user, buf, sizeof(ep->mda_user)); - - if (strcasecmp("sender", field) == 0) - return ascii_load_mailaddr(&ep->sender, buf); - - if (strcasecmp("smtpname", field) == 0) - return ascii_load_string(ep->smtpname, buf, - sizeof(ep->smtpname)); - - if (strcasecmp("sockaddr", field) == 0) - return ascii_load_sockaddr(&ep->ss, buf); - - if (strcasecmp("tag", field) == 0) - return ascii_load_string(ep->tag, buf, sizeof ep->tag); - - if (strcasecmp("type", field) == 0) - return ascii_load_type(&ep->type, buf); - - if (strcasecmp("version", field) == 0) - return ascii_load_uint32(&ep->version, buf); - - if (strcasecmp("dsn-notify", field) == 0) - return ascii_load_uint8(&ep->dsn_notify, buf); - - if (strcasecmp("dsn-orcpt", field) == 0) - return ascii_load_mailaddr(&ep->dsn_orcpt, buf); - - if (strcasecmp("dsn-ret", field) == 0) - return ascii_load_dsn_ret(&ep->dsn_ret, buf); - - if (strcasecmp("dsn-envid", field) == 0) - return ascii_load_string(ep->dsn_envid, buf, - sizeof(ep->dsn_envid)); - - if (strcasecmp("esc-class", field) == 0) - return ascii_load_uint8(&ep->esc_class, buf); - - if (strcasecmp("esc-code", field) == 0) - return ascii_load_uint8(&ep->esc_code, buf); - - return (0); -} - -static int -envelope_ascii_load(struct envelope *ep, struct dict *d) -{ - const char *field; - char *value; - void *hdl; - - hdl = NULL; - while (dict_iter(d, &hdl, &field, (void **)&value)) - if (!ascii_load_field(field, ep, value)) - goto err; - - return (1); - -err: - log_warnx("envelope: invalid field \"%s\"", field); - return (0); -} - - -static int -ascii_dump_uint8(uint8_t src, char *dest, size_t len) -{ - return bsnprintf(dest, len, "%d", src); -} - -static int -ascii_dump_uint16(uint16_t src, char *dest, size_t len) -{ - return bsnprintf(dest, len, "%d", src); -} - -static int -ascii_dump_uint32(uint32_t src, char *dest, size_t len) -{ - return bsnprintf(dest, len, "%d", src); -} - -static int -ascii_dump_time(time_t src, char *dest, size_t len) -{ - return bsnprintf(dest, len, "%lld", (long long) src); -} - -static int -ascii_dump_string(const char *src, char *dest, size_t len) -{ - return bsnprintf(dest, len, "%s", src); -} - -static int -ascii_dump_type(enum delivery_type type, char *dest, size_t len) -{ - char *p = NULL; - - switch (type) { - case D_MDA: - p = "mda"; - break; - case D_MTA: - p = "mta"; - break; - case D_BOUNCE: - p = "bounce"; - break; - default: - return 0; - } - - return bsnprintf(dest, len, "%s", p); -} - -static int -ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len) -{ - return bsnprintf(dest, len, "%s@%s", - addr->user, addr->domain); -} - -static int -ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len) -{ - size_t cpylen = 0; - - buf[0] = '\0'; - if (flags) { - if (flags & EF_AUTHENTICATED) - cpylen = strlcat(buf, "authenticated", len); - if (flags & EF_BOUNCE) { - if (buf[0] != '\0') - (void)strlcat(buf, " ", len); - cpylen = strlcat(buf, "bounce", len); - } - if (flags & EF_INTERNAL) { - if (buf[0] != '\0') - (void)strlcat(buf, " ", len); - cpylen = strlcat(buf, "internal", len); - } - } - - return cpylen < len ? 1 : 0; -} - -static int -ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len) -{ - char *p = NULL; - - switch (type) { - case B_FAILED: - p = "failed"; - break; - case B_DELAYED: - p = "delayed"; - break; - case B_DELIVERED: - p = "delivered"; - break; - default: - return 0; - } - return bsnprintf(dest, len, "%s", p); -} - - -static int -ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len) -{ - size_t cpylen = 0; - - dest[0] = '\0'; - if (flag == DSN_RETFULL) - cpylen = strlcat(dest, "FULL", len); - else if (flag == DSN_RETHDRS) - cpylen = strlcat(dest, "HDRS", len); - - return cpylen < len ? 1 : 0; -} - -static int -ascii_dump_field(const char *field, const struct envelope *ep, - char *buf, size_t len) -{ - if (strcasecmp(field, "dispatcher") == 0) - return ascii_dump_string(ep->dispatcher, buf, len); - - if (strcasecmp(field, "bounce-delay") == 0) { - if (ep->agent.bounce.type != B_DELAYED) - return (1); - return ascii_dump_time(ep->agent.bounce.delay, buf, len); - } - - if (strcasecmp(field, "bounce-ttl") == 0) { - if (ep->agent.bounce.type != B_DELAYED) - return (1); - return ascii_dump_time(ep->agent.bounce.ttl, buf, len); - } - - if (strcasecmp(field, "bounce-type") == 0) - return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len); - - if (strcasecmp(field, "ctime") == 0) - return ascii_dump_time(ep->creation, buf, len); - - if (strcasecmp(field, "dest") == 0) - return ascii_dump_mailaddr(&ep->dest, buf, len); - - if (strcasecmp(field, "username") == 0) { - if (ep->username[0]) - return ascii_dump_string(ep->username, buf, len); - return 1; - } - - if (strcasecmp(field, "errorline") == 0) - return ascii_dump_string(ep->errorline, buf, len); - - if (strcasecmp(field, "ttl") == 0) - return ascii_dump_time(ep->ttl, buf, len); - - if (strcasecmp(field, "flags") == 0) - return ascii_dump_flags(ep->flags, buf, len); - - if (strcasecmp(field, "helo") == 0) - return ascii_dump_string(ep->helo, buf, len); - - if (strcasecmp(field, "hostname") == 0) - return ascii_dump_string(ep->hostname, buf, len); - - if (strcasecmp(field, "last-bounce") == 0) - return ascii_dump_time(ep->lastbounce, buf, len); - - if (strcasecmp(field, "last-try") == 0) - return ascii_dump_time(ep->lasttry, buf, len); - - if (strcasecmp(field, "retry") == 0) - return ascii_dump_uint16(ep->retry, buf, len); - - if (strcasecmp(field, "rcpt") == 0) - return ascii_dump_mailaddr(&ep->rcpt, buf, len); - - if (strcasecmp(field, "mda-exec") == 0) { - if (ep->mda_exec[0]) - return ascii_dump_string(ep->mda_exec, buf, len); - return 1; - } - - if (strcasecmp(field, "mda-subaddress") == 0) { - if (ep->mda_subaddress[0]) - return ascii_dump_string(ep->mda_subaddress, buf, len); - return 1; - } - - if (strcasecmp(field, "mda-user") == 0) { - if (ep->mda_user[0]) - return ascii_dump_string(ep->mda_user, buf, len); - return 1; - } - - if (strcasecmp(field, "sender") == 0) - return ascii_dump_mailaddr(&ep->sender, buf, len); - - if (strcasecmp(field, "smtpname") == 0) - return ascii_dump_string(ep->smtpname, buf, len); - - if (strcasecmp(field, "sockaddr") == 0) - return ascii_dump_string(ss_to_text(&ep->ss), buf, len); - - if (strcasecmp(field, "tag") == 0) - return ascii_dump_string(ep->tag, buf, len); - - if (strcasecmp(field, "type") == 0) - return ascii_dump_type(ep->type, buf, len); - - if (strcasecmp(field, "version") == 0) - return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len); - - if (strcasecmp(field, "dsn-notify") == 0) - return ascii_dump_uint8(ep->dsn_notify, buf, len); - - if (strcasecmp(field, "dsn-ret") == 0) - return ascii_dump_dsn_ret(ep->dsn_ret, buf, len); - - if (strcasecmp(field, "dsn-orcpt") == 0) { - if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0]) - return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len); - return 1; - } - - if (strcasecmp(field, "dsn-envid") == 0) - return ascii_dump_string(ep->dsn_envid, buf, len); - - if (strcasecmp(field, "esc-class") == 0) { - if (ep->esc_class) - return ascii_dump_uint8(ep->esc_class, buf, len); - return 1; - } - - if (strcasecmp(field, "esc-code") == 0) { - /* this is not a pasto, we dump esc_code if esc_class is !0 */ - if (ep->esc_class) - return ascii_dump_uint8(ep->esc_code, buf, len); - return 1; - } - - return (0); -} - -static void -envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len, - const char *field) -{ - char buf[8192]; - int l; - - if (*dest == NULL) - return; - - memset(buf, 0, sizeof buf); - if (!ascii_dump_field(field, ep, buf, sizeof buf)) - goto err; - if (buf[0] == '\0') - return; - - l = snprintf(*dest, *len, "%s: %s\n", field, buf); - if (l < 0 || (size_t) l >= *len) - goto err; - *dest += l; - *len -= l; - - return; -err: - *dest = NULL; -} diff --git a/smtpd/esc.c b/smtpd/esc.c deleted file mode 100644 index 64a44c79..00000000 --- a/smtpd/esc.c +++ /dev/null @@ -1,116 +0,0 @@ -/* $OpenBSD: esc.c,v 1.5 2016/09/03 22:16:39 gilles Exp $ */ - -/* - * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <stdio.h> -#include <limits.h> - -#include "smtpd-defines.h" -#include "smtpd-api.h" - -static struct escode { - enum enhanced_status_code code; - const char *description; -} esc[] = { - /* 0.0 */ - { ESC_OTHER_STATUS, "Other/Undefined" }, - - /* 1.x */ - { ESC_OTHER_ADDRESS_STATUS, "Other/Undefined address status" }, - { ESC_BAD_DESTINATION_MAILBOX_ADDRESS, "Bad destination mailbox address" }, - { ESC_BAD_DESTINATION_SYSTEM_ADDRESS, "Bad destination system address" }, - { ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX, "Bad destination mailbox address syntax" }, - { ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS, "Destination mailbox address ambiguous" }, - { ESC_DESTINATION_ADDRESS_VALID, "Destination address valid" }, - { ESC_DESTINATION_MAILBOX_HAS_MOVED, "Destination mailbox has moved, No forwarding address" }, - { ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX, "Bad sender's mailbox address syntax" }, - { ESC_BAD_SENDER_SYSTEM_ADDRESS, "Bad sender's system address syntax" }, - - /* 2.x */ - { ESC_OTHER_MAILBOX_STATUS, "Other/Undefined mailbox status" }, - { ESC_MAILBOX_DISABLED, "Mailbox disabled, not accepting messages" }, - { ESC_MAILBOX_FULL, "Mailbox full" }, - { ESC_MESSAGE_LENGTH_TOO_LARGE, "Message length exceeds administrative limit" }, - { ESC_MAILING_LIST_EXPANSION_PROBLEM, "Mailing list expansion problem" }, - - /* 3.x */ - { ESC_OTHER_MAIL_SYSTEM_STATUS, "Other/Undefined mail system status" }, - { ESC_MAIL_SYSTEM_FULL, "Mail system full" }, - { ESC_SYSTEM_NOT_ACCEPTING_MESSAGES, "System not accepting network messages" }, - { ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES, "System not capable of selected features" }, - { ESC_MESSAGE_TOO_BIG_FOR_SYSTEM, "Message too big for system" }, - { ESC_SYSTEM_INCORRECTLY_CONFIGURED, "System incorrectly configured" }, - - /* 4.x */ - { ESC_OTHER_NETWORK_ROUTING_STATUS, "Other/Undefined network or routing status" }, - { ESC_NO_ANSWER_FROM_HOST, "No answer from host" }, - { ESC_BAD_CONNECTION, "Bad connection" }, - { ESC_DIRECTORY_SERVER_FAILURE, "Directory server failure" }, - { ESC_UNABLE_TO_ROUTE, "Unable to route" }, - { ESC_MAIL_SYSTEM_CONGESTION, "Mail system congestion" }, - { ESC_ROUTING_LOOP_DETECTED, "Routing loop detected" }, - { ESC_DELIVERY_TIME_EXPIRED, "Delivery time expired" }, - - /* 5.x */ - { ESC_INVALID_RECIPIENT, "Invalid recipient" }, - { ESC_INVALID_COMMAND, "Invalid command" }, - { ESC_SYNTAX_ERROR, "Syntax error" }, - { ESC_TOO_MANY_RECIPIENTS, "Too many recipients" }, - { ESC_INVALID_COMMAND_ARGUMENTS, "Invalid command arguments" }, - { ESC_WRONG_PROTOCOL_VERSION, "Wrong protocol version" }, - - /* 6.x */ - { ESC_OTHER_MEDIA_ERROR, "Other/Undefined media error" }, - { ESC_MEDIA_NOT_SUPPORTED, "Media not supported" }, - { ESC_CONVERSION_REQUIRED_AND_PROHIBITED, "Conversion required and prohibited" }, - { ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED, "Conversion required but not supported" }, - { ESC_CONVERSION_WITH_LOSS_PERFORMED, "Conversion with loss performed" }, - { ESC_CONVERSION_FAILED, "Conversion failed" }, - - /* 7.x */ - { ESC_OTHER_SECURITY_STATUS, "Other/Undefined security status" }, - { ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED, "Delivery not authorized, message refused" }, - { ESC_MAILING_LIST_EXPANSION_PROHIBITED, "Mailing list expansion prohibited" }, - { ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE,"Security conversion required but not possible" }, - { ESC_SECURITY_FEATURES_NOT_SUPPORTED, "Security features not supported" }, - { ESC_CRYPTOGRAPHIC_FAILURE, "Cryptographic failure" }, - { ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED, "Cryptographic algorithm not supported" }, - { ESC_MESSAGE_TOO_BIG_FOR_SYSTEM, "Message integrity failure" }, -}; - -const char * -esc_code(enum enhanced_status_class class, enum enhanced_status_code code) -{ - static char buffer[6]; - - (void)snprintf(buffer, sizeof buffer, "%d.%d.%d", class, code / 10, code % 10); - return buffer; - -} - -const char * -esc_description(enum enhanced_status_code code) -{ - uint32_t i; - - for (i = 0; i < nitems(esc); ++i) - if (code == esc[i].code) - return esc[i].description; - return "Other/Undefined"; -} diff --git a/smtpd/expand.c b/smtpd/expand.c deleted file mode 100644 index a4306fc0..00000000 --- a/smtpd/expand.c +++ /dev/null @@ -1,332 +0,0 @@ -/* $OpenBSD: expand.c,v 1.31 2018/05/31 21:06:12 gilles Exp $ */ - -/* - * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#ifdef HAVE_LIBUTIL_H -#include <libutil.h> -#endif - -#include "smtpd.h" -#include "log.h" - -static const char *expandnode_info(struct expandnode *); - -struct expandnode * -expand_lookup(struct expand *expand, struct expandnode *key) -{ - return RB_FIND(expandtree, &expand->tree, key); -} - -int -expand_to_text(struct expand *expand, char *buf, size_t sz) -{ - struct expandnode *xn; - - buf[0] = '\0'; - - RB_FOREACH(xn, expandtree, &expand->tree) { - if (buf[0]) - (void)strlcat(buf, ", ", sz); - if (strlcat(buf, expandnode_to_text(xn), sz) >= sz) - return 0; - } - - return 1; -} - -void -expand_insert(struct expand *expand, struct expandnode *node) -{ - struct expandnode *xn; - - node->rule = expand->rule; - node->parent = expand->parent; - - log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s", - expand, expandnode_info(node)); - if (node->type == EXPAND_USERNAME && - expand->parent && - expand->parent->type == EXPAND_USERNAME && - !strcmp(expand->parent->u.user, node->u.user)) { - log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1", - expand); - node->sameuser = 1; - } - - if (expand_lookup(expand, node)) { - log_trace(TRACE_EXPAND, "expand: %p: node found, discarding", - expand); - return; - } - - xn = xmemdup(node, sizeof *xn); - xn->rule = expand->rule; - xn->parent = expand->parent; - if (xn->parent) - xn->depth = xn->parent->depth + 1; - else - xn->depth = 0; - RB_INSERT(expandtree, &expand->tree, xn); - if (expand->queue) - TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry); - expand->nb_nodes++; - log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn); -} - -void -expand_clear(struct expand *expand) -{ - struct expandnode *xn; - - log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand); - if (expand->queue) - while ((xn = TAILQ_FIRST(expand->queue))) - TAILQ_REMOVE(expand->queue, xn, tq_entry); - - while ((xn = RB_ROOT(&expand->tree)) != NULL) { - RB_REMOVE(expandtree, &expand->tree, xn); - free(xn); - } -} - -void -expand_free(struct expand *expand) -{ - expand_clear(expand); - - log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand); - free(expand); -} - -int -expand_cmp(struct expandnode *e1, struct expandnode *e2) -{ - struct expandnode *p1, *p2; - int r; - - if (e1->type < e2->type) - return -1; - if (e1->type > e2->type) - return 1; - if (e1->sameuser < e2->sameuser) - return -1; - if (e1->sameuser > e2->sameuser) - return 1; - if (e1->realuser < e2->realuser) - return -1; - if (e1->realuser > e2->realuser) - return 1; - - r = memcmp(&e1->u, &e2->u, sizeof(e1->u)); - if (r) - return (r); - - if (e1->parent == e2->parent) - return (0); - - if (e1->parent == NULL) - return (-1); - if (e2->parent == NULL) - return (1); - - /* - * The same node can be expanded in for different dest context. - * Wen need to distinguish between those. - */ - for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent) - ; - for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent) - ; - if (p1 < p2) - return (-1); - if (p1 > p2) - return (1); - - if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER) - return (0); - - /* - * For external delivery, we need to distinguish between users. - * If we can't find a username, we assume it is _smtpd. - */ - for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent) - ; - for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent) - ; - if (p1 < p2) - return (-1); - if (p1 > p2) - return (1); - - return (0); -} - -static int -expand_line_split(char **line, char **ret) -{ - static char buffer[LINE_MAX]; - int esc, dq, sq; - size_t i; - char *s; - - memset(buffer, 0, sizeof buffer); - esc = dq = sq = 0; - i = 0; - for (s = *line; (*s) && (i < sizeof(buffer)); ++s) { - if (esc) { - buffer[i++] = *s; - esc = 0; - continue; - } - if (*s == '\\') { - esc = 1; - continue; - } - if (*s == ',' && !dq && !sq) { - *ret = buffer; - *line = s+1; - return (1); - } - - buffer[i++] = *s; - esc = 0; - - if (*s == '"' && !sq) - dq ^= 1; - if (*s == '\'' && !dq) - sq ^= 1; - } - - if (esc || dq || sq || i == sizeof(buffer)) - return (-1); - - *ret = buffer; - *line = s; - return (i ? 1 : 0); -} - -int -expand_line(struct expand *expand, const char *s, int do_includes) -{ - struct expandnode xn; - char buffer[LINE_MAX]; - char *p, *subrcpt; - int ret; - - memset(buffer, 0, sizeof buffer); - if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) - return 0; - - p = buffer; - while ((ret = expand_line_split(&p, &subrcpt)) > 0) { - subrcpt = strip(subrcpt); - if (subrcpt[0] == '\0') - continue; - if (!text_to_expandnode(&xn, subrcpt)) - return 0; - if (!do_includes) - if (xn.type == EXPAND_INCLUDE) - continue; - expand_insert(expand, &xn); - } - - if (ret >= 0) - return 1; - - /* expand_line_split() returned < 0 */ - return 0; -} - -static const char * -expandnode_info(struct expandnode *e) -{ - static char buffer[1024]; - const char *type = NULL; - const char *value = NULL; - char tmp[64]; - - switch (e->type) { - case EXPAND_FILTER: - type = "filter"; - break; - case EXPAND_FILENAME: - type = "filename"; - break; - case EXPAND_INCLUDE: - type = "include"; - break; - case EXPAND_USERNAME: - type = "username"; - break; - case EXPAND_ADDRESS: - type = "address"; - break; - case EXPAND_ERROR: - type = "error"; - break; - case EXPAND_INVALID: - default: - return NULL; - } - - if ((value = expandnode_to_text(e)) == NULL) - return NULL; - - (void)strlcpy(buffer, type, sizeof buffer); - (void)strlcat(buffer, ":", sizeof buffer); - if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer) - return NULL; - - (void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent); - if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) - return NULL; - - (void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule); - if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) - return NULL; - - if (e->rule) { - (void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher); - if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) - return NULL; - } - - if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer) - return NULL; - - return buffer; -} - -RB_GENERATE(expandtree, expandnode, entry, expand_cmp); diff --git a/smtpd/filter.c b/smtpd/filter.c deleted file mode 100644 index 614486b7..00000000 --- a/smtpd/filter.c +++ /dev/null @@ -1,868 +0,0 @@ -/* $OpenBSD: filter.c,v 1.25 2017/01/09 09:53:23 reyk Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/wait.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <limits.h> -#include <resolv.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -enum { - QUERY_READY, - QUERY_RUNNING, - QUERY_DONE -}; - - -struct filter_proc { - TAILQ_ENTRY(filter_proc) entry; - struct mproc mproc; - int hooks; - int flags; - int ready; -}; - -struct filter { - TAILQ_ENTRY(filter) entry; - struct filter_proc *proc; -}; -TAILQ_HEAD(filter_lst, filter); - -TAILQ_HEAD(filter_query_lst, filter_query); -struct filter_session { - uint64_t id; - int terminate; - struct filter_lst *filters; - struct filter *fcurr; - - int error; - struct io *iev; - size_t idatalen; - FILE *ofile; - - struct filter_query *eom; -}; - -struct filter_query { - uint64_t qid; - int type; - struct filter_session *session; - - int state; - struct filter *current; - - /* current data */ - union { - struct { - struct sockaddr_storage local; - struct sockaddr_storage remote; - char hostname[HOST_NAME_MAX+1]; - } connect; - char line[LINE_MAX]; - struct mailaddr maddr; - size_t datalen; - } u; - - /* current response */ - struct { - int status; - int code; - char *response; - } smtp; -}; - -static void filter_imsg(struct mproc *, struct imsg *); -static void filter_post_event(uint64_t, int, struct filter *, struct filter *); -static struct filter_query *filter_query(struct filter_session *, int); -static void filter_drain_query(struct filter_query *); -static void filter_run_query(struct filter *, struct filter_query *); -static void filter_end_query(struct filter_query *); -static void filter_set_sink(struct filter_session *, int); -static int filter_tx(struct filter_session *, int); -static void filter_tx_io(struct io *, int, void *); - -static TAILQ_HEAD(, filter_proc) procs; -struct dict chains; - -static const char * filter_session_to_text(struct filter_session *); -static const char * filter_query_to_text(struct filter_query *); -static const char * filter_to_text(struct filter *); -static const char * filter_proc_to_text(struct filter_proc *); -static const char * query_to_str(int); -static const char * event_to_str(int); -static const char * status_to_str(int); -static const char * filterimsg_to_str(int); - -struct tree sessions; -struct tree queries; - -static void -filter_add_arg(struct filter_conf *filter, char *arg) -{ - if (filter->argc == MAX_FILTER_ARGS) { - log_warnx("warn: filter \"%s\" is full", filter->name); - fatalx("exiting"); - } - filter->argv[filter->argc++] = arg; -} - -static void -filter_extend_chain(struct filter_lst *chain, const char *name) -{ - struct filter *n; - struct filter_lst *fchain; - struct filter_conf *fconf; - int i; - - fconf = dict_xget(&env->sc_filters, name); - if (fconf->chain) { - log_debug("filter: extending with \"%s\"", name); - for (i = 0; i < fconf->argc; i++) - filter_extend_chain(chain, fconf->argv[i]); - } - else { - log_debug("filter: adding filter \"%s\"", name); - n = xcalloc(1, sizeof(*n), "filter_extend_chain"); - fchain = dict_get(&chains, name); - n->proc = TAILQ_FIRST(fchain)->proc; - TAILQ_INSERT_TAIL(chain, n, entry); - } -} - -void -filter_postfork(void) -{ - static int prepare = 0; - struct filter_conf *filter; - void *iter; - struct filter_proc *proc; - struct filter_lst *fchain; - struct filter *f; - struct mproc *p; - int done, i; - - if (prepare) - return; - prepare = 1; - - TAILQ_INIT(&procs); - dict_init(&chains); - - log_debug("filter: building simple chains..."); - - /* create all filter proc and associated chains */ - iter = NULL; - while (dict_iter(&env->sc_filters, &iter, NULL, (void **)&filter)) { - if (filter->chain) - continue; - - log_debug("filter: building simple chain \"%s\"", filter->name); - proc = xcalloc(1, sizeof(*proc), "filter_postfork"); - p = &proc->mproc; - p->handler = filter_imsg; - p->proc = PROC_FILTER; - p->name = xstrdup(filter->name, "filter_postfork"); - p->data = proc; - if (tracing & TRACE_DEBUG) - filter_add_arg(filter, "-v"); - if (foreground_log) - filter_add_arg(filter, "-d"); - if (mproc_fork(p, filter->path, filter->argv) < 0) - fatalx("filter_postfork"); - - log_debug("filter: registering proc \"%s\"", filter->name); - f = xcalloc(1, sizeof(*f), "filter_postfork"); - f->proc = proc; - - TAILQ_INSERT_TAIL(&procs, proc, entry); - fchain = xcalloc(1, sizeof(*fchain), "filter_postfork"); - TAILQ_INIT(fchain); - TAILQ_INSERT_TAIL(fchain, f, entry); - dict_xset(&chains, filter->name, fchain); - filter->done = 1; - } - - log_debug("filter: building complex chains..."); - - /* resolve all chains */ - done = 0; - while (!done) { - done = 1; - iter = NULL; - while (dict_iter(&env->sc_filters, &iter, NULL, - (void **)&filter)) { - if (filter->done) - continue; - done = 0; - filter->done = 1; - for (i = 0; i < filter->argc; i++) { - if (!dict_get(&chains, filter->argv[i])) { - filter->done = 0; - break; - } - } - if (filter->done == 0) - continue; - fchain = xcalloc(1, sizeof(*fchain), "filter_postfork"); - TAILQ_INIT(fchain); - log_debug("filter: building chain \"%s\"...", - filter->name); - for (i = 0; i < filter->argc; i++) - filter_extend_chain(fchain, filter->argv[i]); - log_debug("filter: done building chain \"%s\"", - filter->name); - dict_xset(&chains, filter->name, fchain); - } - } - log_debug("filter: done building complex chains"); - - fchain = xcalloc(1, sizeof(*fchain), "filter_postfork"); - TAILQ_INIT(fchain); - dict_xset(&chains, "<no-filter>", fchain); -} - -void -filter_configure(void) -{ - static int init = 0; - struct filter_proc *p; - - if (init) - return; - init = 1; - - tree_init(&sessions); - tree_init(&queries); - - TAILQ_FOREACH(p, &procs, entry) { - m_create(&p->mproc, IMSG_FILTER_REGISTER, 0, 0, -1); - m_add_u32(&p->mproc, FILTER_API_VERSION); - m_add_string(&p->mproc, p->mproc.name); - m_close(&p->mproc); - mproc_enable(&p->mproc); - } - - if (TAILQ_FIRST(&procs) == NULL) - smtp_configure(); -} - -void -filter_event(uint64_t id, int event) -{ - struct filter_session *s; - - if (event == EVENT_DISCONNECT) - /* On disconnect, the session is virtualy dead */ - s = tree_xpop(&sessions, id); - else - s = tree_xget(&sessions, id); - - filter_post_event(id, event, TAILQ_FIRST(s->filters), NULL); - - if (event == EVENT_DISCONNECT) { - if (s->iev) - io_free(s->iev); - if (s->ofile) - fclose(s->ofile); - free(s); - } -} - -void -filter_connect(uint64_t id, const struct sockaddr *local, - const struct sockaddr *remote, const char *host, const char *filter) -{ - struct filter_session *s; - struct filter_query *q; - - s = xcalloc(1, sizeof(*s), "filter_event"); - s->id = id; - if (filter == NULL) - filter = "<no-filter>"; - s->filters = dict_xget(&chains, filter); - tree_xset(&sessions, s->id, s); - - filter_event(id, EVENT_CONNECT); - q = filter_query(s, QUERY_CONNECT); - - memmove(&q->u.connect.local, local, SA_LEN(local)); - memmove(&q->u.connect.remote, remote, SA_LEN(remote)); - strlcpy(q->u.connect.hostname, host, sizeof(q->u.connect.hostname)); - - q->smtp.status = FILTER_OK; - q->smtp.code = 0; - q->smtp.response = NULL; - - filter_drain_query(q); -} - -void -filter_mailaddr(uint64_t id, int type, const struct mailaddr *maddr) -{ - struct filter_session *s; - struct filter_query *q; - - s = tree_xget(&sessions, id); - q = filter_query(s, type); - - strlcpy(q->u.maddr.user, maddr->user, sizeof(q->u.maddr.user)); - strlcpy(q->u.maddr.domain, maddr->domain, sizeof(q->u.maddr.domain)); - - filter_drain_query(q); -} - -void -filter_line(uint64_t id, int type, const char *line) -{ - struct filter_session *s; - struct filter_query *q; - - s = tree_xget(&sessions, id); - q = filter_query(s, type); - - if (line) - strlcpy(q->u.line, line, sizeof(q->u.line)); - - filter_drain_query(q); -} - -void -filter_eom(uint64_t id, int type, size_t datalen) -{ - struct filter_session *s; - struct filter_query *q; - - s = tree_xget(&sessions, id); - q = filter_query(s, type); - q->u.datalen = datalen; - - filter_drain_query(q); -} - -static void -filter_set_sink(struct filter_session *s, int sink) -{ - struct mproc *p; - - while (s->fcurr) { - if (s->fcurr->proc->hooks & HOOK_DATALINE) { - log_trace(TRACE_FILTERS, "filter: sending fd %d to %s", - sink, filter_to_text(s->fcurr)); - p = &s->fcurr->proc->mproc; - m_create(p, IMSG_FILTER_PIPE, 0, 0, sink); - m_add_id(p, s->id); - m_close(p); - return; - } - s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry); - } - - log_trace(TRACE_FILTERS, "filter: chain input is %d", sink); - smtp_filter_fd(s->id, sink); -} - -void -filter_build_fd_chain(uint64_t id, int sink) -{ - struct filter_session *s; - int fd; - - s = tree_xget(&sessions, id); - s->fcurr = TAILQ_LAST(s->filters, filter_lst); - - fd = filter_tx(s, sink); - filter_set_sink(s, fd); -} - -void -filter_post_event(uint64_t id, int event, struct filter *f, struct filter *end) -{ - for(; f && f != end; f = TAILQ_NEXT(f, entry)) { - log_trace(TRACE_FILTERS, "filter: post-event event=%s filter=%s", - event_to_str(event), f->proc->mproc.name); - - m_create(&f->proc->mproc, IMSG_FILTER_EVENT, 0, 0, -1); - m_add_id(&f->proc->mproc, id); - m_add_int(&f->proc->mproc, event); - m_close(&f->proc->mproc); - } -} - -static struct filter_query * -filter_query(struct filter_session *s, int type) -{ - struct filter_query *q; - - q = xcalloc(1, sizeof(*q), "filter_query"); - q->qid = generate_uid(); - q->session = s; - q->type = type; - - q->state = QUERY_READY; - q->current = TAILQ_FIRST(s->filters); - - log_trace(TRACE_FILTERS, "filter: new query %s", query_to_str(type)); - - return (q); -} - -static void -filter_drain_query(struct filter_query *q) -{ - log_trace(TRACE_FILTERS, "filter: filter_drain_query %s", - filter_query_to_text(q)); - - /* - * The query must be passed through all filters that registered - * a hook, until one rejects it. - */ - while (q->state != QUERY_DONE) { - /* Walk over all filters */ - while (q->current) { - filter_run_query(q->current, q); - if (q->state == QUERY_RUNNING) { - log_trace(TRACE_FILTERS, - "filter: waiting for running query %s", - filter_query_to_text(q)); - return; - } - } - q->state = QUERY_DONE; - } - - /* Defer the response if the file is not closed yet. */ - if (q->type == QUERY_EOM && q->session->ofile && q->smtp.status == FILTER_OK) { - log_debug("filter: deferring eom query..."); - q->session->eom = q; - return; - } - - filter_end_query(q); -} - -static void -filter_run_query(struct filter *f, struct filter_query *q) -{ - log_trace(TRACE_FILTERS, - "filter: running filter %s for query %s", - filter_to_text(f), filter_query_to_text(q)); - - m_create(&f->proc->mproc, IMSG_FILTER_QUERY, 0, 0, -1); - m_add_id(&f->proc->mproc, q->session->id); - m_add_id(&f->proc->mproc, q->qid); - m_add_int(&f->proc->mproc, q->type); - - switch (q->type) { - case QUERY_CONNECT: - m_add_sockaddr(&f->proc->mproc, - (struct sockaddr *)&q->u.connect.local); - m_add_sockaddr(&f->proc->mproc, - (struct sockaddr *)&q->u.connect.remote); - m_add_string(&f->proc->mproc, q->u.connect.hostname); - break; - case QUERY_HELO: - m_add_string(&f->proc->mproc, q->u.line); - break; - case QUERY_MAIL: - case QUERY_RCPT: - m_add_mailaddr(&f->proc->mproc, &q->u.maddr); - break; - case QUERY_EOM: - m_add_u32(&f->proc->mproc, q->u.datalen); - break; - default: - break; - } - m_close(&f->proc->mproc); - - tree_xset(&queries, q->qid, q); - q->state = QUERY_RUNNING; -} - -static void -filter_end_query(struct filter_query *q) -{ - struct filter_session *s = q->session; - const char *response = q->smtp.response; - - log_trace(TRACE_FILTERS, "filter: filter_end_query %s", - filter_query_to_text(q)); - - if (q->type == QUERY_EOM && q->smtp.status == FILTER_OK) { - if (s->error || q->u.datalen != s->idatalen) { - response = "Internal error"; - q->smtp.code = 451; - q->smtp.status = FILTER_FAIL; - if (!s->error) - log_warnx("filter: datalen mismatch on session %" PRIx64 - ": %zu/%zu", s->id, s->idatalen, q->u.datalen); - } - } - - log_trace(TRACE_FILTERS, - "filter: query %016"PRIx64" done: " - "status=%s code=%d response=\"%s\"", - q->qid, - status_to_str(q->smtp.status), - q->smtp.code, - response); - - smtp_filter_response(s->id, q->type, q->smtp.status, q->smtp.code, - response); - free(q->smtp.response); - free(q); -} - -static void -filter_imsg(struct mproc *p, struct imsg *imsg) -{ - struct filter_proc *proc = p->data; - struct filter_session *s; - struct filter_query *q; - struct msg m; - const char *line; - uint64_t qid; - uint32_t datalen; - int type, status, code; - - if (imsg == NULL) { - log_warnx("warn: filter \"%s\" closed unexpectedly", p->name); - fatalx("exiting"); - } - - log_trace(TRACE_FILTERS, "filter: imsg %s from procfilter %s", - filterimsg_to_str(imsg->hdr.type), - filter_proc_to_text(proc)); - - switch (imsg->hdr.type) { - - case IMSG_FILTER_REGISTER: - if (proc->ready) { - log_warnx("warn: filter \"%s\" already registered", - proc->mproc.name); - exit(1); - } - - m_msg(&m, imsg); - m_get_int(&m, &proc->hooks); - m_get_int(&m, &proc->flags); - m_end(&m); - proc->ready = 1; - - log_debug("debug: filter \"%s\": hooks 0x%08x flags 0x%04x", - proc->mproc.name, proc->hooks, proc->flags); - - TAILQ_FOREACH(proc, &procs, entry) - if (!proc->ready) - return; - - smtp_configure(); - break; - - case IMSG_FILTER_RESPONSE: - m_msg(&m, imsg); - m_get_id(&m, &qid); - m_get_int(&m, &type); - if (type == QUERY_EOM) - m_get_u32(&m, &datalen); - m_get_int(&m, &status); - m_get_int(&m, &code); - if (m_is_eom(&m)) - line = NULL; - else - m_get_string(&m, &line); - m_end(&m); - - q = tree_xpop(&queries, qid); - if (q->type != type) { - log_warnx("warn: filter: type mismatch %d != %d", - q->type, type); - fatalx("exiting"); - } - q->smtp.status = status; - if (code) - q->smtp.code = code; - if (line) { - free(q->smtp.response); - q->smtp.response = xstrdup(line, "filter_imsg"); - } - q->state = (status == FILTER_OK) ? QUERY_READY : QUERY_DONE; - if (type == QUERY_EOM) - q->u.datalen = datalen; - - q->current = TAILQ_NEXT(q->current, entry); - filter_drain_query(q); - break; - - case IMSG_FILTER_PIPE: - m_msg(&m, imsg); - m_get_id(&m, &qid); - m_end(&m); - - s = tree_xget(&sessions, qid); - s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry); - filter_set_sink(s, imsg->fd); - break; - - default: - log_warnx("warn: bad imsg from filter %s", p->name); - exit(1); - } -} - -static int -filter_tx(struct filter_session *s, int sink) -{ - int sp[2]; - - s->idatalen = 0; - s->eom = NULL; - s->error = 0; - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { - log_warn("warn: filter: socketpair"); - return (-1); - } - - if ((s->ofile = fdopen(sink, "w")) == NULL) { - log_warn("warn: filter: fdopen"); - close(sp[0]); - close(sp[1]); - return (-1); - } - - io_set_nonblocking(sp[0]); - io_set_nonblocking(sp[1]); - - s->iev = io_new(); - io_set_callback(s->iev, filter_tx_io, s); - io_set_fd(s->iev, sp[0]); - io_set_read(s->iev); - - return (sp[1]); -} - -static void -filter_tx_io(struct io *io, int evt, void *arg) -{ - struct filter_session *s = arg; - size_t len, n; - char *data; - - log_trace(TRACE_FILTERS, "filter: filter_tx_io(%p, %s)", - s, io_strevent(evt)); - - switch (evt) { - case IO_DATAIN: - data = io_data(s->iev); - len = io_datalen(s->iev); - - log_trace(TRACE_FILTERS, - "filter: filter_tx_io: datain (%zu) for req %016"PRIx64"", - len, s->id); - - n = fwrite(data, 1, len, s->ofile); - if (n != len) { - log_warnx("warn: filter_tx_io: fwrite %zu/%zu", n, len); - s->error = 1; - break; - } - s->idatalen += n; - io_drop(s->iev, n); - return; - - case IO_DISCONNECTED: - log_trace(TRACE_FILTERS, - "debug: filter: tx done (%zu) for req %016"PRIx64, - s->idatalen, s->id); - break; - - default: - log_warn("warn: filter_tx_io: bad evt (%d) for req %016"PRIx64, - evt, s->id); - s->error = 1; - break; - } - - io_free(s->iev); - s->iev = NULL; - fclose(s->ofile); - s->ofile = NULL; - - /* deferred eom request */ - if (s->eom) { - log_debug("filter: running eom query..."); - filter_end_query(s->eom); - } else { - log_debug("filter: eom not received yet"); - } -} - -static const char * -filter_query_to_text(struct filter_query *q) -{ - static char buf[1024]; - char tmp[1024]; - - tmp[0] = '\0'; - - switch (q->type) { - case QUERY_CONNECT: - strlcat(tmp, "=", sizeof tmp); - strlcat(tmp, ss_to_text(&q->u.connect.local), - sizeof tmp); - strlcat(tmp, " <-> ", sizeof tmp); - strlcat(tmp, ss_to_text(&q->u.connect.remote), - sizeof tmp); - strlcat(tmp, "(", sizeof tmp); - strlcat(tmp, q->u.connect.hostname, sizeof tmp); - strlcat(tmp, ")", sizeof tmp); - break; - case QUERY_MAIL: - case QUERY_RCPT: - snprintf(tmp, sizeof tmp, "=%s@%s", - q->u.maddr.user, q->u.maddr.domain); - break; - case QUERY_HELO: - snprintf(tmp, sizeof tmp, "=%s", q->u.line); - break; - default: - break; - } - snprintf(buf, sizeof buf, "%016"PRIx64"[%s%s,%s]", - q->qid, query_to_str(q->type), tmp, - filter_session_to_text(q->session)); - - return (buf); -} - -static const char * -filter_session_to_text(struct filter_session *s) -{ - static char buf[1024]; - - if (s == NULL) - return "filter_session@NULL"; - - snprintf(buf, sizeof(buf), - "filter_session@%p[datalen=%zu,eom=%p,ofile=%p]", - s, s->idatalen, s->eom, s->ofile); - - return buf; -} - -static const char * -filter_to_text(struct filter *f) -{ - static char buf[1024]; - - snprintf(buf, sizeof buf, "filter:%s", filter_proc_to_text(f->proc)); - - return (buf); -} - -static const char * -filter_proc_to_text(struct filter_proc *proc) -{ - static char buf[1024]; - - snprintf(buf, sizeof buf, "%s[hooks=0x%08x,flags=0x%04x]", - proc->mproc.name, proc->hooks, proc->flags); - - return (buf); -} - -#define CASE(x) case x : return #x - -static const char * -filterimsg_to_str(int imsg) -{ - switch (imsg) { - CASE(IMSG_FILTER_REGISTER); - CASE(IMSG_FILTER_EVENT); - CASE(IMSG_FILTER_QUERY); - CASE(IMSG_FILTER_PIPE); - CASE(IMSG_FILTER_RESPONSE); - default: - return "IMSG_FILTER_???"; - } -} - -static const char * -query_to_str(int query) -{ - switch (query) { - CASE(QUERY_CONNECT); - CASE(QUERY_HELO); - CASE(QUERY_MAIL); - CASE(QUERY_RCPT); - CASE(QUERY_DATA); - CASE(QUERY_EOM); - CASE(QUERY_DATALINE); - default: - return "QUERY_???"; - } -} - -static const char * -event_to_str(int event) -{ - switch (event) { - CASE(EVENT_CONNECT); - CASE(EVENT_RESET); - CASE(EVENT_DISCONNECT); - CASE(EVENT_TX_BEGIN); - CASE(EVENT_TX_COMMIT); - CASE(EVENT_TX_ROLLBACK); - default: - return "EVENT_???"; - } -} - -static const char * -status_to_str(int status) -{ - switch (status) { - CASE(FILTER_OK); - CASE(FILTER_FAIL); - CASE(FILTER_CLOSE); - default: - return "FILTER_???"; - } -} diff --git a/smtpd/forward.5 b/smtpd/forward.5 deleted file mode 100644 index 5a68f229..00000000 --- a/smtpd/forward.5 +++ /dev/null @@ -1,83 +0,0 @@ -.\" $OpenBSD: forward.5,v 1.9 2015/03/13 22:41:54 eric Exp $ -.\" -.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: March 13 2015 $ -.Dt FORWARD 5 -.Os -.Sh NAME -.Nm forward -.Nd email forwarding information file -.Sh DESCRIPTION -Users may put a -.Nm .forward -file in their home directory. -If this file exists, -.Xr smtpd 8 -forwards email to the destinations specified therein. -.Pp -A -.Nm .forward -file contains a list of expansion values, as described in -.Xr aliases 5 . -Each expansion value should be on a line by itself. -However, the -.Nm .forward -mechanism differs from the aliases mechanism in that it disallows -file inclusion -.Pq :include: -and it performs expansion under the user ID of the -.Nm .forward -file owner. -.Pp -Permissions on the -.Nm .forward -file are very strict and expansion is rejected if the file is -group or world-writable; -if the home directory is group writeable; -or if the file is not owned by the user. -.Pp -Users should avoid editing directly the -.Nm .forward -file to prevent delivery failures from occurring if a message -arrives while the file is not fully written. -The best option is to use a temporary file and use the -.Xr mv 1 -command to atomically overwrite the former -.Nm .forward . -Alternatively, setting the -.Xr sticky 8 -bit on the home directory will cause the -.Nm .forward -lookup to return a temporary failure, causing mails to be deferred. -.Sh FILES -.Bl -tag -width "~/.forwardXXX" -compact -.It Pa ~/.forward -Email forwarding information. -.El -.Sh EXAMPLES -The following file forwards mail to -.Dq user@example.com , -and pipes the same mail to -.Dq examplemda . -.Bd -literal -offset indent -# empty lines are ignored - -user@example.com # anything after # is ignored -"|/path/to/examplemda" -.Ed -.Sh SEE ALSO -.Xr aliases 5 , -.Xr smtpd 8 diff --git a/smtpd/forward.c b/smtpd/forward.c deleted file mode 100644 index 7494c6ce..00000000 --- a/smtpd/forward.c +++ /dev/null @@ -1,104 +0,0 @@ -/* $OpenBSD: forward.c,v 1.39 2015/12/28 22:08:30 jung Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#ifdef HAVE_LIBUTIL_H -#include <libutil.h> -#endif -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define MAX_FORWARD_SIZE (4 * 1024) -#define MAX_EXPAND_NODES (100) - -int -forwards_get(int fd, struct expand *expand) -{ - FILE *fp = NULL; - char *line = NULL; - size_t len; - size_t lineno; - size_t save; - int ret; - struct stat sb; - - ret = -1; - if (fstat(fd, &sb) == -1) - goto end; - - /* if it's empty just pretend that no expansion took place */ - if (sb.st_size == 0) { - log_info("info: forward file is empty"); - ret = 0; - goto end; - } - - /* over MAX_FORWARD_SIZE, temporarily fail */ - if (sb.st_size >= MAX_FORWARD_SIZE) { - log_info("info: forward file exceeds max size"); - goto end; - } - - if ((fp = fdopen(fd, "r")) == NULL) { - log_warn("warn: fdopen failure in forwards_get()"); - goto end; - } - - lineno = 0; - save = expand->nb_nodes; - while ((line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { - if (!expand_line(expand, line, 0)) { - log_info("info: parse error in forward file"); - goto end; - } - if (expand->nb_nodes > MAX_EXPAND_NODES) { - log_info("info: forward file expanded too many nodes"); - goto end; - } - free(line); - } - - ret = expand->nb_nodes > save ? 1 : 0; - -end: - free(line); - if (fp) - fclose(fp); - else - close(fd); - return ret; -} diff --git a/smtpd/iobuf.c b/smtpd/iobuf.c deleted file mode 100644 index dec10660..00000000 --- a/smtpd/iobuf.c +++ /dev/null @@ -1,462 +0,0 @@ -/* $OpenBSD: iobuf.c,v 1.13 2020/04/24 11:34:07 eric Exp $ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#ifdef IO_TLS -#include <openssl/err.h> -#include <openssl/ssl.h> -#endif - -#include "iobuf.h" - -#define IOBUF_MAX 65536 -#define IOBUFQ_MIN 4096 - -struct ioqbuf *ioqbuf_alloc(struct iobuf *, size_t); -void iobuf_drain(struct iobuf *, size_t); - -int -iobuf_init(struct iobuf *io, size_t size, size_t max) -{ - memset(io, 0, sizeof *io); - - if (max == 0) - max = IOBUF_MAX; - - if (size == 0) - size = max; - - if (size > max) - return (-1); - - if ((io->buf = calloc(size, 1)) == NULL) - return (-1); - - io->size = size; - io->max = max; - - return (0); -} - -void -iobuf_clear(struct iobuf *io) -{ - struct ioqbuf *q; - - free(io->buf); - - while ((q = io->outq)) { - io->outq = q->next; - free(q); - } - - memset(io, 0, sizeof (*io)); -} - -void -iobuf_drain(struct iobuf *io, size_t n) -{ - struct ioqbuf *q; - size_t left = n; - - while ((q = io->outq) && left) { - if ((q->wpos - q->rpos) > left) { - q->rpos += left; - left = 0; - } else { - left -= q->wpos - q->rpos; - io->outq = q->next; - free(q); - } - } - - io->queued -= (n - left); - if (io->outq == NULL) - io->outqlast = NULL; -} - -int -iobuf_extend(struct iobuf *io, size_t n) -{ - char *t; - - if (n > io->max) - return (-1); - - if (io->max - io->size < n) - return (-1); - - t = recallocarray(io->buf, io->size, io->size + n, 1); - if (t == NULL) - return (-1); - - io->size += n; - io->buf = t; - - return (0); -} - -size_t -iobuf_left(struct iobuf *io) -{ - return io->size - io->wpos; -} - -size_t -iobuf_space(struct iobuf *io) -{ - return io->size - (io->wpos - io->rpos); -} - -size_t -iobuf_len(struct iobuf *io) -{ - return io->wpos - io->rpos; -} - -char * -iobuf_data(struct iobuf *io) -{ - return io->buf + io->rpos; -} - -void -iobuf_drop(struct iobuf *io, size_t n) -{ - if (n >= iobuf_len(io)) { - io->rpos = io->wpos = 0; - return; - } - - io->rpos += n; -} - -char * -iobuf_getline(struct iobuf *iobuf, size_t *rlen) -{ - char *buf; - size_t len, i; - - buf = iobuf_data(iobuf); - len = iobuf_len(iobuf); - - for (i = 0; i + 1 <= len; i++) - if (buf[i] == '\n') { - /* Note: the returned address points into the iobuf - * buffer. We NUL-end it for convenience, and discard - * the data from the iobuf, so that the caller doesn't - * have to do it. The data remains "valid" as long - * as the iobuf does not overwrite it, that is until - * the next call to iobuf_normalize() or iobuf_extend(). - */ - iobuf_drop(iobuf, i + 1); - buf[i] = '\0'; - if (rlen) - *rlen = i; - return (buf); - } - - return (NULL); -} - -void -iobuf_normalize(struct iobuf *io) -{ - if (io->rpos == 0) - return; - - if (io->rpos == io->wpos) { - io->rpos = io->wpos = 0; - return; - } - - memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos); - io->wpos -= io->rpos; - io->rpos = 0; -} - -ssize_t -iobuf_read(struct iobuf *io, int fd) -{ - ssize_t n; - - n = read(fd, io->buf + io->wpos, iobuf_left(io)); - if (n == -1) { - /* XXX is this really what we want? */ - if (errno == EAGAIN || errno == EINTR) - return (IOBUF_WANT_READ); - return (IOBUF_ERROR); - } - if (n == 0) - return (IOBUF_CLOSED); - - io->wpos += n; - - return (n); -} - -struct ioqbuf * -ioqbuf_alloc(struct iobuf *io, size_t len) -{ - struct ioqbuf *q; - - if (len < IOBUFQ_MIN) - len = IOBUFQ_MIN; - - if ((q = malloc(sizeof(*q) + len)) == NULL) - return (NULL); - - q->rpos = 0; - q->wpos = 0; - q->size = len; - q->next = NULL; - q->buf = (char *)(q) + sizeof(*q); - - if (io->outqlast == NULL) - io->outq = q; - else - io->outqlast->next = q; - io->outqlast = q; - - return (q); -} - -size_t -iobuf_queued(struct iobuf *io) -{ - return io->queued; -} - -void * -iobuf_reserve(struct iobuf *io, size_t len) -{ - struct ioqbuf *q; - void *r; - - if (len == 0) - return (NULL); - - if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) { - if ((q = ioqbuf_alloc(io, len)) == NULL) - return (NULL); - } - - r = q->buf + q->wpos; - q->wpos += len; - io->queued += len; - - return (r); -} - -int -iobuf_queue(struct iobuf *io, const void *data, size_t len) -{ - void *buf; - - if (len == 0) - return (0); - - if ((buf = iobuf_reserve(io, len)) == NULL) - return (-1); - - memmove(buf, data, len); - - return (len); -} - -int -iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt) -{ - int i; - size_t len = 0; - char *buf; - - for (i = 0; i < iovcnt; i++) - len += iov[i].iov_len; - - if ((buf = iobuf_reserve(io, len)) == NULL) - return (-1); - - for (i = 0; i < iovcnt; i++) { - if (iov[i].iov_len == 0) - continue; - memmove(buf, iov[i].iov_base, iov[i].iov_len); - buf += iov[i].iov_len; - } - - return (0); - -} - -int -iobuf_fqueue(struct iobuf *io, const char *fmt, ...) -{ - va_list ap; - int len; - - va_start(ap, fmt); - len = iobuf_vfqueue(io, fmt, ap); - va_end(ap); - - return (len); -} - -int -iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap) -{ - char *buf; - int len; - - len = vasprintf(&buf, fmt, ap); - - if (len == -1) - return (-1); - - len = iobuf_queue(io, buf, len); - free(buf); - - return (len); -} - -ssize_t -iobuf_write(struct iobuf *io, int fd) -{ - struct iovec iov[IOV_MAX]; - struct ioqbuf *q; - int i; - ssize_t n; - - i = 0; - for (q = io->outq; q ; q = q->next) { - if (i >= IOV_MAX) - break; - iov[i].iov_base = q->buf + q->rpos; - iov[i].iov_len = q->wpos - q->rpos; - i++; - } - - n = writev(fd, iov, i); - if (n == -1) { - if (errno == EAGAIN || errno == EINTR) - return (IOBUF_WANT_WRITE); - if (errno == EPIPE) - return (IOBUF_CLOSED); - return (IOBUF_ERROR); - } - - iobuf_drain(io, n); - - return (n); -} - -int -iobuf_flush(struct iobuf *io, int fd) -{ - ssize_t s; - - while (io->queued) - if ((s = iobuf_write(io, fd)) < 0) - return (s); - - return (0); -} - -#ifdef IO_TLS - -int -iobuf_flush_tls(struct iobuf *io, void *tls) -{ - ssize_t s; - - while (io->queued) - if ((s = iobuf_write_tls(io, tls)) < 0) - return (s); - - return (0); -} - -ssize_t -iobuf_write_tls(struct iobuf *io, void *tls) -{ - struct ioqbuf *q; - int r; - ssize_t n; - - q = io->outq; - n = SSL_write(tls, q->buf + q->rpos, q->wpos - q->rpos); - if (n <= 0) { - switch ((r = SSL_get_error(tls, n))) { - case SSL_ERROR_WANT_READ: - return (IOBUF_WANT_READ); - case SSL_ERROR_WANT_WRITE: - return (IOBUF_WANT_WRITE); - case SSL_ERROR_ZERO_RETURN: /* connection closed */ - return (IOBUF_CLOSED); - case SSL_ERROR_SYSCALL: - if (ERR_peek_last_error()) - return (IOBUF_TLSERROR); - return (IOBUF_ERROR); - default: - return (IOBUF_TLSERROR); - } - } - iobuf_drain(io, n); - - return (n); -} - -ssize_t -iobuf_read_tls(struct iobuf *io, void *tls) -{ - ssize_t n; - int r; - - n = SSL_read(tls, io->buf + io->wpos, iobuf_left(io)); - if (n < 0) { - switch ((r = SSL_get_error(tls, n))) { - case SSL_ERROR_WANT_READ: - return (IOBUF_WANT_READ); - case SSL_ERROR_WANT_WRITE: - return (IOBUF_WANT_WRITE); - case SSL_ERROR_SYSCALL: - if (ERR_peek_last_error()) - return (IOBUF_TLSERROR); - return (IOBUF_ERROR); - default: - return (IOBUF_TLSERROR); - } - } else if (n == 0) - return (IOBUF_CLOSED); - - io->wpos += n; - - return (n); -} - -#endif /* IO_TLS */ diff --git a/smtpd/iobuf.h b/smtpd/iobuf.h deleted file mode 100644 index c454d0a1..00000000 --- a/smtpd/iobuf.h +++ /dev/null @@ -1,67 +0,0 @@ -/* $OpenBSD: iobuf.h,v 1.5 2019/06/12 17:42:53 eric Exp $ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -struct ioqbuf { - struct ioqbuf *next; - char *buf; - size_t size; - size_t wpos; - size_t rpos; -}; - -struct iobuf { - char *buf; - size_t max; - size_t size; - size_t wpos; - size_t rpos; - - size_t queued; - struct ioqbuf *outq; - struct ioqbuf *outqlast; -}; - -#define IOBUF_WANT_READ -1 -#define IOBUF_WANT_WRITE -2 -#define IOBUF_CLOSED -3 -#define IOBUF_ERROR -4 -#define IOBUF_TLSERROR -5 - -int iobuf_init(struct iobuf *, size_t, size_t); -void iobuf_clear(struct iobuf *); - -int iobuf_extend(struct iobuf *, size_t); -void iobuf_normalize(struct iobuf *); -void iobuf_drop(struct iobuf *, size_t); -size_t iobuf_space(struct iobuf *); -size_t iobuf_len(struct iobuf *); -size_t iobuf_left(struct iobuf *); -char *iobuf_data(struct iobuf *); -char *iobuf_getline(struct iobuf *, size_t *); -ssize_t iobuf_read(struct iobuf *, int); -ssize_t iobuf_read_tls(struct iobuf *, void *); - -size_t iobuf_queued(struct iobuf *); -void* iobuf_reserve(struct iobuf *, size_t); -int iobuf_queue(struct iobuf *, const void*, size_t); -int iobuf_queuev(struct iobuf *, const struct iovec *, int); -int iobuf_fqueue(struct iobuf *, const char *, ...); -int iobuf_vfqueue(struct iobuf *, const char *, va_list); -int iobuf_flush(struct iobuf *, int); -int iobuf_flush_tls(struct iobuf *, void *); -ssize_t iobuf_write(struct iobuf *, int); -ssize_t iobuf_write_tls(struct iobuf *, void *); diff --git a/smtpd/ioev.c b/smtpd/ioev.c deleted file mode 100644 index e0a8a096..00000000 --- a/smtpd/ioev.c +++ /dev/null @@ -1,1064 +0,0 @@ -/* $OpenBSD: ioev.c,v 1.42 2019/06/12 17:42:53 eric Exp $ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/socket.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <inttypes.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> - -#include "ioev.h" -#include "iobuf.h" - -#ifdef IO_TLS -#include <openssl/err.h> -#include <openssl/ssl.h> -#endif - -enum { - IO_STATE_NONE, - IO_STATE_CONNECT, - IO_STATE_CONNECT_TLS, - IO_STATE_ACCEPT_TLS, - IO_STATE_UP, - - IO_STATE_MAX, -}; - -#define IO_PAUSE_IN IO_IN -#define IO_PAUSE_OUT IO_OUT -#define IO_READ 0x04 -#define IO_WRITE 0x08 -#define IO_RW (IO_READ | IO_WRITE) -#define IO_RESET 0x10 /* internal */ -#define IO_HELD 0x20 /* internal */ - -struct io { - int sock; - void *arg; - void (*cb)(struct io*, int, void *); - struct iobuf iobuf; - size_t lowat; - int timeout; - int flags; - int state; - struct event ev; - void *tls; - const char *error; /* only valid immediately on callback */ -}; - -const char* io_strflags(int); -const char* io_evstr(short); - -void _io_init(void); -void io_hold(struct io *); -void io_release(struct io *); -void io_callback(struct io*, int); -void io_dispatch(int, short, void *); -void io_dispatch_connect(int, short, void *); -size_t io_pending(struct io *); -size_t io_queued(struct io*); -void io_reset(struct io *, short, void (*)(int, short, void*)); -void io_frame_enter(const char *, struct io *, int); -void io_frame_leave(struct io *); - -#ifdef IO_TLS -void ssl_error(const char *); /* XXX external */ - -static const char* io_tls_error(void); -void io_dispatch_accept_tls(int, short, void *); -void io_dispatch_connect_tls(int, short, void *); -void io_dispatch_read_tls(int, short, void *); -void io_dispatch_write_tls(int, short, void *); -void io_reload_tls(struct io *io); -#endif - -static struct io *current = NULL; -static uint64_t frame = 0; -static int _io_debug = 0; - -#define io_debug(args...) do { if (_io_debug) printf(args); } while(0) - - -const char* -io_strio(struct io *io) -{ - static char buf[128]; - char ssl[128]; - - ssl[0] = '\0'; -#ifdef IO_TLS - if (io->tls) { - (void)snprintf(ssl, sizeof ssl, " tls=%s:%s:%d", - SSL_get_version(io->tls), - SSL_get_cipher_name(io->tls), - SSL_get_cipher_bits(io->tls, NULL)); - } -#endif - - (void)snprintf(buf, sizeof buf, - "<io:%p fd=%d to=%d fl=%s%s ib=%zu ob=%zu>", - io, io->sock, io->timeout, io_strflags(io->flags), ssl, - io_pending(io), io_queued(io)); - - return (buf); -} - -#define CASE(x) case x : return #x - -const char* -io_strevent(int evt) -{ - static char buf[32]; - - switch (evt) { - CASE(IO_CONNECTED); - CASE(IO_TLSREADY); - CASE(IO_DATAIN); - CASE(IO_LOWAT); - CASE(IO_DISCONNECTED); - CASE(IO_TIMEOUT); - CASE(IO_ERROR); - default: - (void)snprintf(buf, sizeof(buf), "IO_? %d", evt); - return buf; - } -} - -void -io_set_nonblocking(int fd) -{ - int flags; - - if ((flags = fcntl(fd, F_GETFL)) == -1) - err(1, "io_set_blocking:fcntl(F_GETFL)"); - - flags |= O_NONBLOCK; - - if (fcntl(fd, F_SETFL, flags) == -1) - err(1, "io_set_blocking:fcntl(F_SETFL)"); -} - -void -io_set_nolinger(int fd) -{ - struct linger l; - - memset(&l, 0, sizeof(l)); - if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1) - err(1, "io_set_linger:setsockopt()"); -} - -/* - * Event framing must not rely on an io pointer to refer to the "same" io - * throughout the frame, because this is not always the case: - * - * 1) enter(addr0) -> free(addr0) -> leave(addr0) = SEGV - * 2) enter(addr0) -> free(addr0) -> malloc == addr0 -> leave(addr0) = BAD! - * - * In both case, the problem is that the io is freed in the callback, so - * the pointer becomes invalid. If that happens, the user is required to - * call io_clear, so we can adapt the frame state there. - */ -void -io_frame_enter(const char *where, struct io *io, int ev) -{ - io_debug("\n=== %" PRIu64 " ===\n" - "io_frame_enter(%s, %s, %s)\n", - frame, where, io_evstr(ev), io_strio(io)); - - if (current) - errx(1, "io_frame_enter: interleaved frames"); - - current = io; - - io_hold(io); -} - -void -io_frame_leave(struct io *io) -{ - io_debug("io_frame_leave(%" PRIu64 ")\n", frame); - - if (current && current != io) - errx(1, "io_frame_leave: io mismatch"); - - /* io has been cleared */ - if (current == NULL) - goto done; - - /* TODO: There is a possible optimization there: - * In a typical half-duplex request/response scenario, - * the io is waiting to read a request, and when done, it queues - * the response in the output buffer and goes to write mode. - * There, the write event is set and will be triggered in the next - * event frame. In most case, the write call could be done - * immediately as part of the last read frame, thus avoiding to go - * through the event loop machinery. So, as an optimisation, we - * could detect that case here and force an event dispatching. - */ - - /* Reload the io if it has not been reset already. */ - io_release(io); - current = NULL; - done: - io_debug("=== /%" PRIu64 "\n", frame); - - frame += 1; -} - -void -_io_init() -{ - static int init = 0; - - if (init) - return; - - init = 1; - _io_debug = getenv("IO_DEBUG") != NULL; -} - -struct io * -io_new(void) -{ - struct io *io; - - _io_init(); - - if ((io = calloc(1, sizeof(*io))) == NULL) - return NULL; - - io->sock = -1; - io->timeout = -1; - - if (iobuf_init(&io->iobuf, 0, 0) == -1) { - free(io); - return NULL; - } - - return io; -} - -void -io_free(struct io *io) -{ - io_debug("io_clear(%p)\n", io); - - /* the current io is virtually dead */ - if (io == current) - current = NULL; - -#ifdef IO_TLS - SSL_free(io->tls); - io->tls = NULL; -#endif - - if (event_initialized(&io->ev)) - event_del(&io->ev); - if (io->sock != -1) { - close(io->sock); - io->sock = -1; - } - - iobuf_clear(&io->iobuf); - free(io); -} - -void -io_hold(struct io *io) -{ - io_debug("io_enter(%p)\n", io); - - if (io->flags & IO_HELD) - errx(1, "io_hold: io is already held"); - - io->flags &= ~IO_RESET; - io->flags |= IO_HELD; -} - -void -io_release(struct io *io) -{ - if (!(io->flags & IO_HELD)) - errx(1, "io_release: io is not held"); - - io->flags &= ~IO_HELD; - if (!(io->flags & IO_RESET)) - io_reload(io); -} - -void -io_set_fd(struct io *io, int fd) -{ - io->sock = fd; - if (fd != -1) - io_reload(io); -} - -void -io_set_callback(struct io *io, void(*cb)(struct io *, int, void *), void *arg) -{ - io->cb = cb; - io->arg = arg; -} - -void -io_set_timeout(struct io *io, int msec) -{ - io_debug("io_set_timeout(%p, %d)\n", io, msec); - - io->timeout = msec; -} - -void -io_set_lowat(struct io *io, size_t lowat) -{ - io_debug("io_set_lowat(%p, %zu)\n", io, lowat); - - io->lowat = lowat; -} - -void -io_pause(struct io *io, int dir) -{ - io_debug("io_pause(%p, %x)\n", io, dir); - - io->flags |= dir & (IO_PAUSE_IN | IO_PAUSE_OUT); - io_reload(io); -} - -void -io_resume(struct io *io, int dir) -{ - io_debug("io_resume(%p, %x)\n", io, dir); - - io->flags &= ~(dir & (IO_PAUSE_IN | IO_PAUSE_OUT)); - io_reload(io); -} - -void -io_set_read(struct io *io) -{ - int mode; - - io_debug("io_set_read(%p)\n", io); - - mode = io->flags & IO_RW; - if (!(mode == 0 || mode == IO_WRITE)) - errx(1, "io_set_read(): full-duplex or reading"); - - io->flags &= ~IO_RW; - io->flags |= IO_READ; - io_reload(io); -} - -void -io_set_write(struct io *io) -{ - int mode; - - io_debug("io_set_write(%p)\n", io); - - mode = io->flags & IO_RW; - if (!(mode == 0 || mode == IO_READ)) - errx(1, "io_set_write(): full-duplex or writing"); - - io->flags &= ~IO_RW; - io->flags |= IO_WRITE; - io_reload(io); -} - -const char * -io_error(struct io *io) -{ - return io->error; -} - -void * -io_tls(struct io *io) -{ - return io->tls; -} - -int -io_fileno(struct io *io) -{ - return io->sock; -} - -int -io_paused(struct io *io, int what) -{ - return (io->flags & (IO_PAUSE_IN | IO_PAUSE_OUT)) == what; -} - -/* - * Buffered output functions - */ - -int -io_write(struct io *io, const void *buf, size_t len) -{ - int r; - - r = iobuf_queue(&io->iobuf, buf, len); - - io_reload(io); - - return r; -} - -int -io_writev(struct io *io, const struct iovec *iov, int iovcount) -{ - int r; - - r = iobuf_queuev(&io->iobuf, iov, iovcount); - - io_reload(io); - - return r; -} - -int -io_print(struct io *io, const char *s) -{ - return io_write(io, s, strlen(s)); -} - -int -io_printf(struct io *io, const char *fmt, ...) -{ - va_list ap; - int r; - - va_start(ap, fmt); - r = io_vprintf(io, fmt, ap); - va_end(ap); - - return r; -} - -int -io_vprintf(struct io *io, const char *fmt, va_list ap) -{ - - char *buf; - int len; - - len = vasprintf(&buf, fmt, ap); - if (len == -1) - return -1; - len = io_write(io, buf, len); - free(buf); - - return len; -} - -size_t -io_queued(struct io *io) -{ - return iobuf_queued(&io->iobuf); -} - -/* - * Buffered input functions - */ - -void * -io_data(struct io *io) -{ - return iobuf_data(&io->iobuf); -} - -size_t -io_datalen(struct io *io) -{ - return iobuf_len(&io->iobuf); -} - -char * -io_getline(struct io *io, size_t *sz) -{ - return iobuf_getline(&io->iobuf, sz); -} - -void -io_drop(struct io *io, size_t sz) -{ - return iobuf_drop(&io->iobuf, sz); -} - - -#define IO_READING(io) (((io)->flags & IO_RW) != IO_WRITE) -#define IO_WRITING(io) (((io)->flags & IO_RW) != IO_READ) - -/* - * Setup the necessary events as required by the current io state, - * honouring duplex mode and i/o pauses. - */ -void -io_reload(struct io *io) -{ - short events; - - /* io will be reloaded at release time */ - if (io->flags & IO_HELD) - return; - - iobuf_normalize(&io->iobuf); - -#ifdef IO_TLS - if (io->tls) { - io_reload_tls(io); - return; - } -#endif - - io_debug("io_reload(%p)\n", io); - - events = 0; - if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) - events = EV_READ; - if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io)) - events |= EV_WRITE; - - io_reset(io, events, io_dispatch); -} - -/* Set the requested event. */ -void -io_reset(struct io *io, short events, void (*dispatch)(int, short, void*)) -{ - struct timeval tv, *ptv; - - io_debug("io_reset(%p, %s, %p) -> %s\n", - io, io_evstr(events), dispatch, io_strio(io)); - - /* - * Indicate that the event has already been reset so that reload - * is not called on frame_leave. - */ - io->flags |= IO_RESET; - - if (event_initialized(&io->ev)) - event_del(&io->ev); - - /* - * The io is paused by the user, so we don't want the timeout to be - * effective. - */ - if (events == 0) - return; - - event_set(&io->ev, io->sock, events, dispatch, io); - if (io->timeout >= 0) { - tv.tv_sec = io->timeout / 1000; - tv.tv_usec = (io->timeout % 1000) * 1000; - ptv = &tv; - } else - ptv = NULL; - - event_add(&io->ev, ptv); -} - -size_t -io_pending(struct io *io) -{ - return iobuf_len(&io->iobuf); -} - -const char* -io_strflags(int flags) -{ - static char buf[64]; - - buf[0] = '\0'; - - switch (flags & IO_RW) { - case 0: - (void)strlcat(buf, "rw", sizeof buf); - break; - case IO_READ: - (void)strlcat(buf, "R", sizeof buf); - break; - case IO_WRITE: - (void)strlcat(buf, "W", sizeof buf); - break; - case IO_RW: - (void)strlcat(buf, "RW", sizeof buf); - break; - } - - if (flags & IO_PAUSE_IN) - (void)strlcat(buf, ",F_PI", sizeof buf); - if (flags & IO_PAUSE_OUT) - (void)strlcat(buf, ",F_PO", sizeof buf); - - return buf; -} - -const char* -io_evstr(short ev) -{ - static char buf[64]; - char buf2[16]; - int n; - - n = 0; - buf[0] = '\0'; - - if (ev == 0) { - (void)strlcat(buf, "<NONE>", sizeof(buf)); - return buf; - } - - if (ev & EV_TIMEOUT) { - (void)strlcat(buf, "EV_TIMEOUT", sizeof(buf)); - ev &= ~EV_TIMEOUT; - n++; - } - - if (ev & EV_READ) { - if (n) - (void)strlcat(buf, "|", sizeof(buf)); - (void)strlcat(buf, "EV_READ", sizeof(buf)); - ev &= ~EV_READ; - n++; - } - - if (ev & EV_WRITE) { - if (n) - (void)strlcat(buf, "|", sizeof(buf)); - (void)strlcat(buf, "EV_WRITE", sizeof(buf)); - ev &= ~EV_WRITE; - n++; - } - - if (ev & EV_SIGNAL) { - if (n) - (void)strlcat(buf, "|", sizeof(buf)); - (void)strlcat(buf, "EV_SIGNAL", sizeof(buf)); - ev &= ~EV_SIGNAL; - n++; - } - - if (ev) { - if (n) - (void)strlcat(buf, "|", sizeof(buf)); - (void)strlcat(buf, "EV_?=0x", sizeof(buf)); - (void)snprintf(buf2, sizeof(buf2), "%hx", ev); - (void)strlcat(buf, buf2, sizeof(buf)); - } - - return buf; -} - -void -io_dispatch(int fd, short ev, void *humppa) -{ - struct io *io = humppa; - size_t w; - ssize_t n; - int saved_errno; - - io_frame_enter("io_dispatch", io, ev); - - if (ev == EV_TIMEOUT) { - io_callback(io, IO_TIMEOUT); - goto leave; - } - - if (ev & EV_WRITE && (w = io_queued(io))) { - if ((n = iobuf_write(&io->iobuf, io->sock)) < 0) { - if (n == IOBUF_WANT_WRITE) /* kqueue bug? */ - goto read; - if (n == IOBUF_CLOSED) - io_callback(io, IO_DISCONNECTED); - else { - saved_errno = errno; - io->error = strerror(errno); - errno = saved_errno; - io_callback(io, IO_ERROR); - } - goto leave; - } - if (w > io->lowat && w - n <= io->lowat) - io_callback(io, IO_LOWAT); - } - read: - - if (ev & EV_READ) { - iobuf_normalize(&io->iobuf); - if ((n = iobuf_read(&io->iobuf, io->sock)) < 0) { - if (n == IOBUF_CLOSED) - io_callback(io, IO_DISCONNECTED); - else { - saved_errno = errno; - io->error = strerror(errno); - errno = saved_errno; - io_callback(io, IO_ERROR); - } - goto leave; - } - if (n) - io_callback(io, IO_DATAIN); - } - -leave: - io_frame_leave(io); -} - -void -io_callback(struct io *io, int evt) -{ - io->cb(io, evt, io->arg); -} - -int -io_connect(struct io *io, const struct sockaddr *sa, const struct sockaddr *bsa) -{ - int sock, errno_save; - - if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) - goto fail; - - io_set_nonblocking(sock); - io_set_nolinger(sock); - - if (bsa && bind(sock, bsa, SA_LEN(bsa)) == -1) - goto fail; - - if (connect(sock, sa, SA_LEN(sa)) == -1) - if (errno != EINPROGRESS) - goto fail; - - io->sock = sock; - io_reset(io, EV_WRITE, io_dispatch_connect); - - return (sock); - - fail: - if (sock != -1) { - errno_save = errno; - close(sock); - errno = errno_save; - io->error = strerror(errno); - } - return (-1); -} - -void -io_dispatch_connect(int fd, short ev, void *humppa) -{ - struct io *io = humppa; - int r, e; - socklen_t sl; - - io_frame_enter("io_dispatch_connect", io, ev); - - if (ev == EV_TIMEOUT) { - close(fd); - io->sock = -1; - io_callback(io, IO_TIMEOUT); - } else { - sl = sizeof(e); - r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &e, &sl); - if (r == -1) { - warn("io_dispatch_connect: getsockopt"); - e = errno; - } - if (e) { - close(fd); - io->sock = -1; - io->error = strerror(e); - io_callback(io, e == ETIMEDOUT ? IO_TIMEOUT : IO_ERROR); - } - else { - io->state = IO_STATE_UP; - io_callback(io, IO_CONNECTED); - } - } - - io_frame_leave(io); -} - -#ifdef IO_TLS - -static const char* -io_tls_error(void) -{ - static char buf[128]; - unsigned long e; - - e = ERR_peek_last_error(); - if (e) { - ERR_error_string(e, buf); - return (buf); - } - - return ("No TLS error"); -} - -int -io_start_tls(struct io *io, void *tls) -{ - int mode; - - mode = io->flags & IO_RW; - if (mode == 0 || mode == IO_RW) - errx(1, "io_start_tls(): full-duplex or unset"); - - if (io->tls) - errx(1, "io_start_tls(): TLS already started"); - io->tls = tls; - - if (SSL_set_fd(io->tls, io->sock) == 0) { - ssl_error("io_start_tls:SSL_set_fd"); - return (-1); - } - - if (mode == IO_WRITE) { - io->state = IO_STATE_CONNECT_TLS; - SSL_set_connect_state(io->tls); - io_reset(io, EV_WRITE, io_dispatch_connect_tls); - } else { - io->state = IO_STATE_ACCEPT_TLS; - SSL_set_accept_state(io->tls); - io_reset(io, EV_READ, io_dispatch_accept_tls); - } - - return (0); -} - -void -io_dispatch_accept_tls(int fd, short event, void *humppa) -{ - struct io *io = humppa; - int e, ret; - - io_frame_enter("io_dispatch_accept_tls", io, event); - - if (event == EV_TIMEOUT) { - io_callback(io, IO_TIMEOUT); - goto leave; - } - - if ((ret = SSL_accept(io->tls)) > 0) { - io->state = IO_STATE_UP; - io_callback(io, IO_TLSREADY); - goto leave; - } - - switch ((e = SSL_get_error(io->tls, ret))) { - case SSL_ERROR_WANT_READ: - io_reset(io, EV_READ, io_dispatch_accept_tls); - break; - case SSL_ERROR_WANT_WRITE: - io_reset(io, EV_WRITE, io_dispatch_accept_tls); - break; - default: - io->error = io_tls_error(); - ssl_error("io_dispatch_accept_tls:SSL_accept"); - io_callback(io, IO_ERROR); - break; - } - - leave: - io_frame_leave(io); -} - -void -io_dispatch_connect_tls(int fd, short event, void *humppa) -{ - struct io *io = humppa; - int e, ret; - - io_frame_enter("io_dispatch_connect_tls", io, event); - - if (event == EV_TIMEOUT) { - io_callback(io, IO_TIMEOUT); - goto leave; - } - - if ((ret = SSL_connect(io->tls)) > 0) { - io->state = IO_STATE_UP; - io_callback(io, IO_TLSREADY); - goto leave; - } - - switch ((e = SSL_get_error(io->tls, ret))) { - case SSL_ERROR_WANT_READ: - io_reset(io, EV_READ, io_dispatch_connect_tls); - break; - case SSL_ERROR_WANT_WRITE: - io_reset(io, EV_WRITE, io_dispatch_connect_tls); - break; - default: - io->error = io_tls_error(); - ssl_error("io_dispatch_connect_ssl:SSL_connect"); - io_callback(io, IO_TLSERROR); - break; - } - - leave: - io_frame_leave(io); -} - -void -io_dispatch_read_tls(int fd, short event, void *humppa) -{ - struct io *io = humppa; - int n, saved_errno; - - io_frame_enter("io_dispatch_read_tls", io, event); - - if (event == EV_TIMEOUT) { - io_callback(io, IO_TIMEOUT); - goto leave; - } - -again: - iobuf_normalize(&io->iobuf); - switch ((n = iobuf_read_tls(&io->iobuf, (SSL*)io->tls))) { - case IOBUF_WANT_READ: - io_reset(io, EV_READ, io_dispatch_read_tls); - break; - case IOBUF_WANT_WRITE: - io_reset(io, EV_WRITE, io_dispatch_read_tls); - break; - case IOBUF_CLOSED: - io_callback(io, IO_DISCONNECTED); - break; - case IOBUF_ERROR: - saved_errno = errno; - io->error = strerror(errno); - errno = saved_errno; - io_callback(io, IO_ERROR); - break; - case IOBUF_TLSERROR: - io->error = io_tls_error(); - ssl_error("io_dispatch_read_tls:SSL_read"); - io_callback(io, IO_ERROR); - break; - default: - io_debug("io_dispatch_read_tls(...) -> r=%d\n", n); - io_callback(io, IO_DATAIN); - if (current == io && IO_READING(io) && SSL_pending(io->tls)) - goto again; - } - - leave: - io_frame_leave(io); -} - -void -io_dispatch_write_tls(int fd, short event, void *humppa) -{ - struct io *io = humppa; - int n, saved_errno; - size_t w2, w; - - io_frame_enter("io_dispatch_write_tls", io, event); - - if (event == EV_TIMEOUT) { - io_callback(io, IO_TIMEOUT); - goto leave; - } - - w = io_queued(io); - switch ((n = iobuf_write_tls(&io->iobuf, (SSL*)io->tls))) { - case IOBUF_WANT_READ: - io_reset(io, EV_READ, io_dispatch_write_tls); - break; - case IOBUF_WANT_WRITE: - io_reset(io, EV_WRITE, io_dispatch_write_tls); - break; - case IOBUF_CLOSED: - io_callback(io, IO_DISCONNECTED); - break; - case IOBUF_ERROR: - saved_errno = errno; - io->error = strerror(errno); - errno = saved_errno; - io_callback(io, IO_ERROR); - break; - case IOBUF_TLSERROR: - io->error = io_tls_error(); - ssl_error("io_dispatch_write_tls:SSL_write"); - io_callback(io, IO_ERROR); - break; - default: - io_debug("io_dispatch_write_tls(...) -> w=%d\n", n); - w2 = io_queued(io); - if (w > io->lowat && w2 <= io->lowat) - io_callback(io, IO_LOWAT); - break; - } - - leave: - io_frame_leave(io); -} - -void -io_reload_tls(struct io *io) -{ - short ev = 0; - void (*dispatch)(int, short, void*) = NULL; - - switch (io->state) { - case IO_STATE_CONNECT_TLS: - ev = EV_WRITE; - dispatch = io_dispatch_connect_tls; - break; - case IO_STATE_ACCEPT_TLS: - ev = EV_READ; - dispatch = io_dispatch_accept_tls; - break; - case IO_STATE_UP: - ev = 0; - if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) { - ev = EV_READ; - dispatch = io_dispatch_read_tls; - } - else if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && - io_queued(io)) { - ev = EV_WRITE; - dispatch = io_dispatch_write_tls; - } - if (!ev) - return; /* paused */ - break; - default: - errx(1, "io_reload_tls(): bad state"); - } - - io_reset(io, ev, dispatch); -} - -#endif /* IO_TLS */ diff --git a/smtpd/ioev.h b/smtpd/ioev.h deleted file mode 100644 index f155a7fc..00000000 --- a/smtpd/ioev.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $OpenBSD: ioev.h,v 1.18 2019/09/11 04:19:19 martijn Exp $ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -enum { - IO_CONNECTED = 0, /* connection successful */ - IO_TLSREADY, /* TLS started successfully */ - IO_TLSERROR, /* XXX - needs more work */ - IO_DATAIN, /* new data in input buffer */ - IO_LOWAT, /* output queue running low */ - IO_DISCONNECTED, /* error? */ - IO_TIMEOUT, /* error? */ - IO_ERROR, /* details? */ -}; - -#define IO_IN 0x01 -#define IO_OUT 0x02 - -struct io; - -void io_set_nonblocking(int); -void io_set_nolinger(int); - -struct io *io_new(void); -void io_free(struct io *); -void io_set_read(struct io *); -void io_set_write(struct io *); -void io_set_fd(struct io *, int); -void io_set_callback(struct io *io, void(*)(struct io *, int, void *), void *); -void io_set_timeout(struct io *, int); -void io_set_lowat(struct io *, size_t); -void io_pause(struct io *, int); -void io_resume(struct io *, int); -void io_reload(struct io *); -int io_connect(struct io *, const struct sockaddr *, const struct sockaddr *); -int io_start_tls(struct io *, void *); -const char* io_strio(struct io *); -const char* io_strevent(int); -const char* io_error(struct io *); -void* io_tls(struct io *); -int io_fileno(struct io *); -int io_paused(struct io *, int); - -/* Buffered output functions */ -int io_write(struct io *, const void *, size_t); -int io_writev(struct io *, const struct iovec *, int); -int io_print(struct io *, const char *); -int io_printf(struct io *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -int io_vprintf(struct io *, const char *, va_list); -size_t io_queued(struct io *); - -/* Buffered input functions */ -void* io_data(struct io *); -size_t io_datalen(struct io *); -char* io_getline(struct io *, size_t *); -void io_drop(struct io *, size_t); diff --git a/smtpd/libressl.c b/smtpd/libressl.c deleted file mode 100644 index 57d74389..00000000 --- a/smtpd/libressl.c +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ - -/* - * SSL operations needed when running in a privilege separated environment. - * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard . - */ - -#include "includes.h" - -#include <sys/types.h> - -#include <limits.h> -#include <unistd.h> -#include <stdio.h> - -#include <openssl/err.h> -#include <openssl/bio.h> -#include <openssl/objects.h> -#include <openssl/evp.h> -#include <openssl/x509.h> -#include <openssl/pem.h> -#include <openssl/ssl.h> - -#include "log.h" -#include "ssl.h" - -#define SSL_ECDH_CURVE "prime256v1" - -/* - * Read a bio that contains our certificate in "PEM" format, - * possibly followed by a sequence of CA certificates that should be - * sent to the peer in the Certificate message. - */ -static int -ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in) -{ - int ret = 0; - X509 *x = NULL; - - ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */ - - x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - if (x == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB); - goto end; - } - - ret = SSL_CTX_use_certificate(ctx, x); - - if (ERR_peek_error() != 0) - ret = 0; - /* Key/certificate mismatch doesn't imply ret==0 ... */ - if (ret) { - /* - * If we could set up our certificate, now proceed to - * the CA certificates. - */ - X509 *ca; - int r; - unsigned long err; - - if (ctx->extra_certs != NULL) { - sk_X509_pop_free(ctx->extra_certs, X509_free); - ctx->extra_certs = NULL; - } - - while ((ca = PEM_read_bio_X509(in, NULL, - ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata)) != NULL) { - r = SSL_CTX_add_extra_chain_cert(ctx, ca); - if (!r) { - X509_free(ca); - ret = 0; - goto end; - } - /* - * Note that we must not free r if it was successfully - * added to the chain (while we must free the main - * certificate, since its reference count is increased - * by SSL_CTX_use_certificate). - */ - } - - /* When the while loop ends, it's usually just EOF. */ - err = ERR_peek_last_error(); - if (ERR_GET_LIB(err) == ERR_LIB_PEM && - ERR_GET_REASON(err) == PEM_R_NO_START_LINE) - ERR_clear_error(); - else - ret = 0; /* some real error */ - } - -end: - if (x != NULL) - X509_free(x); - return (ret); -} - -int -SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len) -{ - BIO *in; - int ret = 0; - - in = BIO_new_mem_buf(buf, len); - if (in == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB); - goto end; - } - - ret = ssl_ctx_use_certificate_chain_bio(ctx, in); - -end: - BIO_free(in); - return (ret); -} - -#ifndef HAVE_SSL_CTX_SET_ECDH_AUTO -void -SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int enable) -{ - int nid; - EC_KEY *ecdh; - - if (!enable) - return; - - if ((nid = OBJ_sn2nid(SSL_ECDH_CURVE)) == 0) { - ssl_error("ssl_set_ecdh_auto"); - fatal("ssl_set_ecdh_auto: unknown curve name " - SSL_ECDH_CURVE); - } - - if ((ecdh = EC_KEY_new_by_curve_name(nid)) == NULL) { - ssl_error("ssl_set_ecdh_auto"); - fatal("ssl_set_ecdh_auto: unable to create curve " - SSL_ECDH_CURVE); - } - - SSL_CTX_set_tmp_ecdh(ctx, ecdh); - SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); - EC_KEY_free(ecdh); -} -#endif - -#ifndef HAVE_SSL_CTX_SET_DH_AUTO -void -SSL_CTX_set_dh_auto(SSL_CTX *ctx, int enable) -{ - if (!enable) - return; - - /* stub until OpenSSL catches up with this ... */ - log_warnx("OpenSSL does not support SSL_CTX_set_dh_auto (yet ?)"); - return; -} -#endif diff --git a/smtpd/limit.c b/smtpd/limit.c deleted file mode 100644 index 25e7a026..00000000 --- a/smtpd/limit.c +++ /dev/null @@ -1,124 +0,0 @@ -/* $OpenBSD: limit.c,v 1.5 2016/06/15 19:59:03 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - -void -limit_mta_set_defaults(struct mta_limits *limits) -{ - limits->maxconn_per_host = 10; - limits->maxconn_per_route = 5; - limits->maxconn_per_source = 100; - limits->maxconn_per_connector = 20; - limits->maxconn_per_relay = 100; - limits->maxconn_per_domain = 100; - - limits->conndelay_host = 0; - limits->conndelay_route = 5; - limits->conndelay_source = 0; - limits->conndelay_connector = 0; - limits->conndelay_relay = 2; - limits->conndelay_domain = 0; - - limits->discdelay_route = 3; - - limits->max_mail_per_session = 100; - limits->sessdelay_transaction = 0; - limits->sessdelay_keepalive = 10; - - limits->max_failures_per_session = 25; - - limits->family = AF_UNSPEC; - - limits->task_hiwat = 50; - limits->task_lowat = 30; - limits->task_release = 10; -} - -int -limit_mta_set(struct mta_limits *limits, const char *key, int64_t value) -{ - if (!strcmp(key, "max-conn-per-host")) - limits->maxconn_per_host = value; - else if (!strcmp(key, "max-conn-per-route")) - limits->maxconn_per_route = value; - else if (!strcmp(key, "max-conn-per-source")) - limits->maxconn_per_source = value; - else if (!strcmp(key, "max-conn-per-connector")) - limits->maxconn_per_connector = value; - else if (!strcmp(key, "max-conn-per-relay")) - limits->maxconn_per_relay = value; - else if (!strcmp(key, "max-conn-per-domain")) - limits->maxconn_per_domain = value; - - else if (!strcmp(key, "conn-delay-host")) - limits->conndelay_host = value; - else if (!strcmp(key, "conn-delay-route")) - limits->conndelay_route = value; - else if (!strcmp(key, "conn-delay-source")) - limits->conndelay_source = value; - else if (!strcmp(key, "conn-delay-connector")) - limits->conndelay_connector = value; - else if (!strcmp(key, "conn-delay-relay")) - limits->conndelay_relay = value; - else if (!strcmp(key, "conn-delay-domain")) - limits->conndelay_domain = value; - - else if (!strcmp(key, "reconn-delay-route")) - limits->discdelay_route = value; - - else if (!strcmp(key, "session-mail-max")) - limits->max_mail_per_session = value; - else if (!strcmp(key, "session-transaction-delay")) - limits->sessdelay_transaction = value; - else if (!strcmp(key, "session-keepalive")) - limits->sessdelay_keepalive = value; - - else if (!strcmp(key, "max-failures-per-session")) - limits->max_failures_per_session = value; - - else if (!strcmp(key, "task-hiwat")) - limits->task_hiwat = value; - else if (!strcmp(key, "task-lowat")) - limits->task_lowat = value; - else if (!strcmp(key, "task-release")) - limits->task_release = value; - - else - return (0); - - return (1); -} diff --git a/smtpd/lka.c b/smtpd/lka.c deleted file mode 100644 index 6ac21245..00000000 --- a/smtpd/lka.c +++ /dev/null @@ -1,914 +0,0 @@ -/* $OpenBSD: lka.c,v 1.243 2019/12/21 10:23:37 gilles Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@faurot.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/wait.h> -#include <sys/uio.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <netdb.h> -#include <grp.h> /* needed for setgroups */ -#include <imsg.h> -#include <openssl/err.h> -#include <openssl/ssl.h> -#include <pwd.h> -#include <resolv.h> -#include <limits.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -static void lka_imsg(struct mproc *, struct imsg *); -static void lka_shutdown(void); -static void lka_sig_handler(int, short, void *); -static int lka_authenticate(const char *, const char *, const char *); -static int lka_credentials(const char *, const char *, char *, size_t); -static int lka_userinfo(const char *, const char *, struct userinfo *); -static int lka_addrname(const char *, const struct sockaddr *, - struct addrname *); -static int lka_mailaddrmap(const char *, const char *, const struct mailaddr *); - -static void proc_timeout(int fd, short event, void *p); - -struct event ev_proc_ready; - -static void -lka_imsg(struct mproc *p, struct imsg *imsg) -{ - struct table *table; - int ret; - struct sockaddr_storage ss; - struct userinfo userinfo; - struct addrname addrname; - struct envelope evp; - struct mailaddr maddr; - struct msg m; - union lookup lk; - char buf[LINE_MAX]; - const char *tablename, *username, *password, *label, *procname; - uint64_t reqid; - int v; - struct timeval tv; - const char *direction; - const char *rdns; - const char *command; - const char *response; - const char *ciphers; - const char *address; - const char *domain; - const char *helomethod; - const char *heloname; - const char *filter_name; - const char *result; - struct sockaddr_storage ss_src, ss_dest; - int filter_response; - int filter_phase; - const char *filter_param; - uint32_t msgid; - uint32_t subsystems; - uint64_t evpid; - size_t msgsz; - int ok; - int fcrdns; - - if (imsg == NULL) - lka_shutdown(); - - switch (imsg->hdr.type) { - - case IMSG_GETADDRINFO: - case IMSG_GETNAMEINFO: - case IMSG_RES_QUERY: - resolver_dispatch_request(p, imsg); - return; - - case IMSG_CERT_INIT: - case IMSG_CERT_CERTIFICATE: - case IMSG_CERT_VERIFY: - cert_dispatch_request(p, imsg); - return; - - case IMSG_MTA_DNS_HOST: - case IMSG_MTA_DNS_MX: - case IMSG_MTA_DNS_MX_PREFERENCE: - dns_imsg(p, imsg); - return; - - case IMSG_SMTP_CHECK_SENDER: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_get_string(&m, &username); - m_get_mailaddr(&m, &maddr); - m_end(&m); - - ret = lka_mailaddrmap(tablename, username, &maddr); - - m_create(p, IMSG_SMTP_CHECK_SENDER, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, ret); - m_close(p); - return; - - case IMSG_SMTP_EXPAND_RCPT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_envelope(&m, &evp); - m_end(&m); - lka_session(reqid, &evp); - return; - - case IMSG_SMTP_LOOKUP_HELO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_get_sockaddr(&m, (struct sockaddr *)&ss); - m_end(&m); - - ret = lka_addrname(tablename, (struct sockaddr*)&ss, - &addrname); - - m_create(p, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, ret); - if (ret == LKA_OK) - m_add_string(p, addrname.name); - m_close(p); - return; - - case IMSG_SMTP_AUTHENTICATE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_get_string(&m, &username); - m_get_string(&m, &password); - m_end(&m); - - if (!tablename[0]) { - m_create(p_parent, IMSG_LKA_AUTHENTICATE, - 0, 0, -1); - m_add_id(p_parent, reqid); - m_add_string(p_parent, username); - m_add_string(p_parent, password); - m_close(p_parent); - return; - } - - ret = lka_authenticate(tablename, username, password); - - m_create(p, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, ret); - m_close(p); - return; - - case IMSG_MDA_LOOKUP_USERINFO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_get_string(&m, &username); - m_end(&m); - - ret = lka_userinfo(tablename, username, &userinfo); - - m_create(p, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, ret); - if (ret == LKA_OK) - m_add_data(p, &userinfo, sizeof(userinfo)); - m_close(p); - return; - - case IMSG_MTA_LOOKUP_CREDENTIALS: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_get_string(&m, &label); - m_end(&m); - - lka_credentials(tablename, label, buf, sizeof(buf)); - - m_create(p, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1); - m_add_id(p, reqid); - m_add_string(p, buf); - m_close(p); - return; - - case IMSG_MTA_LOOKUP_SOURCE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_end(&m); - - table = table_find(env, tablename); - - m_create(p, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1); - m_add_id(p, reqid); - - if (table == NULL) { - log_warn("warn: source address table %s missing", - tablename); - m_add_int(p, LKA_TEMPFAIL); - } - else { - ret = table_fetch(table, K_SOURCE, &lk); - if (ret == -1) - m_add_int(p, LKA_TEMPFAIL); - else if (ret == 0) - m_add_int(p, LKA_PERMFAIL); - else { - m_add_int(p, LKA_OK); - m_add_sockaddr(p, - (struct sockaddr *)&lk.source.addr); - } - } - m_close(p); - return; - - case IMSG_MTA_LOOKUP_HELO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &tablename); - m_get_sockaddr(&m, (struct sockaddr *)&ss); - m_end(&m); - - ret = lka_addrname(tablename, (struct sockaddr*)&ss, - &addrname); - - m_create(p, IMSG_MTA_LOOKUP_HELO, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, ret); - if (ret == LKA_OK) - m_add_string(p, addrname.name); - m_close(p); - return; - - case IMSG_MTA_LOOKUP_SMARTHOST: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &domain); - m_get_string(&m, &tablename); - m_end(&m); - - table = table_find(env, tablename); - - m_create(p, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1); - m_add_id(p, reqid); - - if (table == NULL) { - log_warn("warn: smarthost table %s missing", tablename); - m_add_int(p, LKA_TEMPFAIL); - } - else { - if (domain == NULL) - ret = table_fetch(table, K_RELAYHOST, &lk); - else - ret = table_lookup(table, K_RELAYHOST, domain, &lk); - - if (ret == -1) - m_add_int(p, LKA_TEMPFAIL); - else if (ret == 0) - m_add_int(p, LKA_PERMFAIL); - else { - m_add_int(p, LKA_OK); - m_add_string(p, lk.relayhost); - } - } - m_close(p); - return; - - case IMSG_CONF_START: - return; - - case IMSG_CONF_END: - if (tracing & TRACE_TABLES) - table_dump_all(env); - - /* fork & exec tables that need it */ - table_open_all(env); - -#if HAVE_PLEDGE - /* revoke proc & exec */ - if (pledge("stdio rpath inet dns getpw recvfd sendfd", - NULL) == -1) - err(1, "pledge"); -#endif - - /* setup proc registering task */ - evtimer_set(&ev_proc_ready, proc_timeout, &ev_proc_ready); - tv.tv_sec = 0; - tv.tv_usec = 10; - evtimer_add(&ev_proc_ready, &tv); - return; - - case IMSG_LKA_OPEN_FORWARD: - lka_session_forward_reply(imsg->data, imsg->fd); - return; - - case IMSG_LKA_AUTHENTICATE: - imsg->hdr.type = IMSG_SMTP_AUTHENTICATE; - m_forward(p_pony, imsg); - return; - - case IMSG_CTL_VERBOSE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - log_trace_verbose(v); - return; - - case IMSG_CTL_PROFILE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - profiling = v; - return; - - case IMSG_CTL_UPDATE_TABLE: - ret = 0; - table = table_find(env, imsg->data); - if (table == NULL) { - log_warnx("warn: Lookup table not found: " - "\"%s\"", (char *)imsg->data); - } else - ret = table_update(table); - - m_compose(p_control, - (ret == 1) ? IMSG_CTL_OK : IMSG_CTL_FAIL, - imsg->hdr.peerid, 0, -1, NULL, 0); - return; - - case IMSG_LKA_PROCESSOR_FORK: - m_msg(&m, imsg); - m_get_string(&m, &procname); - m_get_u32(&m, &subsystems); - m_end(&m); - - m_create(p, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, -1); - m_add_string(p, procname); - m_close(p); - - lka_proc_forked(procname, subsystems, imsg->fd); - return; - - case IMSG_LKA_PROCESSOR_ERRFD: - m_msg(&m, imsg); - m_get_string(&m, &procname); - m_end(&m); - - lka_proc_errfd(procname, imsg->fd); - shutdown(imsg->fd, SHUT_WR); - return; - - case IMSG_REPORT_SMTP_LINK_CONNECT: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &rdns); - m_get_int(&m, &fcrdns); - m_get_sockaddr(&m, (struct sockaddr *)&ss_src); - m_get_sockaddr(&m, (struct sockaddr *)&ss_dest); - m_end(&m); - - lka_report_smtp_link_connect(direction, &tv, reqid, rdns, fcrdns, &ss_src, &ss_dest); - return; - - case IMSG_REPORT_SMTP_LINK_GREETING: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &domain); - m_end(&m); - - lka_report_smtp_link_greeting(direction, reqid, &tv, domain); - return; - - case IMSG_REPORT_SMTP_LINK_DISCONNECT: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_end(&m); - - lka_report_smtp_link_disconnect(direction, &tv, reqid); - return; - - case IMSG_REPORT_SMTP_LINK_IDENTIFY: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &helomethod); - m_get_string(&m, &heloname); - m_end(&m); - - lka_report_smtp_link_identify(direction, &tv, reqid, helomethod, heloname); - return; - - case IMSG_REPORT_SMTP_LINK_TLS: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &ciphers); - m_end(&m); - - lka_report_smtp_link_tls(direction, &tv, reqid, ciphers); - return; - - case IMSG_REPORT_SMTP_LINK_AUTH: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &username); - m_get_string(&m, &result); - m_end(&m); - - lka_report_smtp_link_auth(direction, &tv, reqid, username, result); - return; - - case IMSG_REPORT_SMTP_TX_RESET: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_end(&m); - - lka_report_smtp_tx_reset(direction, &tv, reqid, msgid); - return; - - case IMSG_REPORT_SMTP_TX_BEGIN: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_end(&m); - - lka_report_smtp_tx_begin(direction, &tv, reqid, msgid); - return; - - case IMSG_REPORT_SMTP_TX_MAIL: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_get_string(&m, &address); - m_get_int(&m, &ok); - m_end(&m); - - lka_report_smtp_tx_mail(direction, &tv, reqid, msgid, address, ok); - return; - - case IMSG_REPORT_SMTP_TX_RCPT: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_get_string(&m, &address); - m_get_int(&m, &ok); - m_end(&m); - - lka_report_smtp_tx_rcpt(direction, &tv, reqid, msgid, address, ok); - return; - - case IMSG_REPORT_SMTP_TX_ENVELOPE: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_get_id(&m, &evpid); - m_end(&m); - - lka_report_smtp_tx_envelope(direction, &tv, reqid, msgid, evpid); - return; - - case IMSG_REPORT_SMTP_TX_DATA: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_get_int(&m, &ok); - m_end(&m); - - lka_report_smtp_tx_data(direction, &tv, reqid, msgid, ok); - return; - - case IMSG_REPORT_SMTP_TX_COMMIT: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_get_size(&m, &msgsz); - m_end(&m); - - lka_report_smtp_tx_commit(direction, &tv, reqid, msgid, msgsz); - return; - - case IMSG_REPORT_SMTP_TX_ROLLBACK: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_u32(&m, &msgid); - m_end(&m); - - lka_report_smtp_tx_rollback(direction, &tv, reqid, msgid); - return; - - case IMSG_REPORT_SMTP_PROTOCOL_CLIENT: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &command); - m_end(&m); - - lka_report_smtp_protocol_client(direction, &tv, reqid, command); - return; - - case IMSG_REPORT_SMTP_PROTOCOL_SERVER: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_string(&m, &response); - m_end(&m); - - lka_report_smtp_protocol_server(direction, &tv, reqid, response); - return; - - case IMSG_REPORT_SMTP_FILTER_RESPONSE: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_get_int(&m, &filter_phase); - m_get_int(&m, &filter_response); - m_get_string(&m, &filter_param); - m_end(&m); - - lka_report_smtp_filter_response(direction, &tv, reqid, - filter_phase, filter_response, filter_param); - return; - - case IMSG_REPORT_SMTP_TIMEOUT: - m_msg(&m, imsg); - m_get_string(&m, &direction); - m_get_timeval(&m, &tv); - m_get_id(&m, &reqid); - m_end(&m); - - lka_report_smtp_timeout(direction, &tv, reqid); - return; - - case IMSG_FILTER_SMTP_PROTOCOL: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &filter_phase); - m_get_string(&m, &filter_param); - m_end(&m); - - lka_filter_protocol(reqid, filter_phase, filter_param); - return; - - case IMSG_FILTER_SMTP_BEGIN: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &filter_name); - m_end(&m); - - lka_filter_begin(reqid, filter_name); - return; - - case IMSG_FILTER_SMTP_END: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - lka_filter_end(reqid); - return; - - case IMSG_FILTER_SMTP_DATA_BEGIN: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - lka_filter_data_begin(reqid); - return; - - case IMSG_FILTER_SMTP_DATA_END: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - lka_filter_data_end(reqid); - return; - - } - - errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -static void -lka_sig_handler(int sig, short event, void *p) -{ - int status; - pid_t pid; - - switch (sig) { - case SIGCHLD: - do { - pid = waitpid(-1, &status, WNOHANG); - } while (pid > 0 || (pid == -1 && errno == EINTR)); - break; - default: - fatalx("lka_sig_handler: unexpected signal"); - } -} - -void -lka_shutdown(void) -{ - log_debug("debug: lookup agent exiting"); - _exit(0); -} - -int -lka(void) -{ - struct passwd *pw; - struct event ev_sigchld; - - purge_config(PURGE_LISTENERS); - - if ((pw = getpwnam(SMTPD_USER)) == NULL) - fatalx("unknown user " SMTPD_USER); - - config_process(PROC_LKA); - - if (initgroups(pw->pw_name, pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("lka: cannot drop privileges"); - - imsg_callback = lka_imsg; - event_init(); - - signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, NULL); - signal_add(&ev_sigchld, NULL); - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - - config_peer(PROC_PARENT); - config_peer(PROC_QUEUE); - config_peer(PROC_CONTROL); - config_peer(PROC_PONY); - - /* Ignore them until we get our config */ - mproc_disable(p_pony); - - lka_report_init(); - lka_filter_init(); - -#if HAVE_PLEDGE - /* proc & exec will be revoked before serving requests */ - if (pledge("stdio rpath inet dns getpw recvfd sendfd proc exec", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} - -static void -proc_timeout(int fd, short event, void *p) -{ - struct event *ev = p; - struct timeval tv; - - if (!lka_proc_ready()) - goto reset; - - lka_filter_ready(); - mproc_enable(p_pony); - return; - -reset: - tv.tv_sec = 0; - tv.tv_usec = 10; - evtimer_add(ev, &tv); -} - - -static int -lka_authenticate(const char *tablename, const char *user, const char *password) -{ - struct table *table; - union lookup lk; - - log_debug("debug: lka: authenticating for %s:%s", tablename, user); - table = table_find(env, tablename); - if (table == NULL) { - log_warnx("warn: could not find table %s needed for authentication", - tablename); - return (LKA_TEMPFAIL); - } - - switch (table_lookup(table, K_CREDENTIALS, user, &lk)) { - case -1: - log_warnx("warn: user credentials lookup fail for %s:%s", - tablename, user); - return (LKA_TEMPFAIL); - case 0: - return (LKA_PERMFAIL); - default: - if (crypt_checkpass(password, lk.creds.password) == 0) - return (LKA_OK); - return (LKA_PERMFAIL); - } -} - -static int -lka_credentials(const char *tablename, const char *label, char *dst, size_t sz) -{ - struct table *table; - union lookup lk; - char *buf; - int buflen, r; - - table = table_find(env, tablename); - if (table == NULL) { - log_warnx("warn: credentials table %s missing", tablename); - return (LKA_TEMPFAIL); - } - - dst[0] = '\0'; - - switch (table_lookup(table, K_CREDENTIALS, label, &lk)) { - case -1: - log_warnx("warn: credentials lookup fail for %s:%s", - tablename, label); - return (LKA_TEMPFAIL); - case 0: - log_warnx("warn: credentials not found for %s:%s", - tablename, label); - return (LKA_PERMFAIL); - default: - if ((buflen = asprintf(&buf, "%c%s%c%s", '\0', - lk.creds.username, '\0', lk.creds.password)) == -1) { - log_warn("warn"); - return (LKA_TEMPFAIL); - } - - r = base64_encode((unsigned char *)buf, buflen, dst, sz); - free(buf); - - if (r == -1) { - log_warnx("warn: credentials parse error for %s:%s", - tablename, label); - return (LKA_TEMPFAIL); - } - return (LKA_OK); - } -} - -static int -lka_userinfo(const char *tablename, const char *username, struct userinfo *res) -{ - struct table *table; - union lookup lk; - - log_debug("debug: lka: userinfo %s:%s", tablename, username); - table = table_find(env, tablename); - if (table == NULL) { - log_warnx("warn: cannot find user table %s", tablename); - return (LKA_TEMPFAIL); - } - - switch (table_lookup(table, K_USERINFO, username, &lk)) { - case -1: - log_warnx("warn: failure during userinfo lookup %s:%s", - tablename, username); - return (LKA_TEMPFAIL); - case 0: - return (LKA_PERMFAIL); - default: - *res = lk.userinfo; - return (LKA_OK); - } -} - -static int -lka_addrname(const char *tablename, const struct sockaddr *sa, - struct addrname *res) -{ - struct table *table; - union lookup lk; - const char *source; - - source = sa_to_text(sa); - - log_debug("debug: lka: helo %s:%s", tablename, source); - table = table_find(env, tablename); - if (table == NULL) { - log_warnx("warn: cannot find helo table %s", tablename); - return (LKA_TEMPFAIL); - } - - switch (table_lookup(table, K_ADDRNAME, source, &lk)) { - case -1: - log_warnx("warn: failure during helo lookup %s:%s", - tablename, source); - return (LKA_TEMPFAIL); - case 0: - return (LKA_PERMFAIL); - default: - *res = lk.addrname; - return (LKA_OK); - } -} - -static int -lka_mailaddrmap(const char *tablename, const char *username, const struct mailaddr *maddr) -{ - struct table *table; - struct maddrnode *mn; - union lookup lk; - int found; - - log_debug("debug: lka: mailaddrmap %s:%s", tablename, username); - table = table_find(env, tablename); - if (table == NULL) { - log_warnx("warn: cannot find mailaddrmap table %s", tablename); - return (LKA_TEMPFAIL); - } - - switch (table_lookup(table, K_MAILADDRMAP, username, &lk)) { - case -1: - log_warnx("warn: failure during mailaddrmap lookup %s:%s", - tablename, username); - return (LKA_TEMPFAIL); - case 0: - return (LKA_PERMFAIL); - default: - found = 0; - TAILQ_FOREACH(mn, &lk.maddrmap->queue, entries) { - if (!mailaddr_match(maddr, &mn->mailaddr)) - continue; - found = 1; - break; - } - maddrmap_free(lk.maddrmap); - if (found) - return (LKA_OK); - return (LKA_PERMFAIL); - } - return (LKA_OK); -} diff --git a/smtpd/lka_filter.c b/smtpd/lka_filter.c deleted file mode 100644 index 2dc66057..00000000 --- a/smtpd/lka_filter.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* $OpenBSD: lka_filter.c,v 1.62 2020/04/24 11:34:07 eric Exp $ */ - -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - -#define PROTOCOL_VERSION "0.6" - -struct filter; -struct filter_session; -static void filter_protocol_internal(struct filter_session *, uint64_t *, uint64_t, enum filter_phase, const char *); -static void filter_protocol(uint64_t, enum filter_phase, const char *); -static void filter_protocol_next(uint64_t, uint64_t, enum filter_phase); -static void filter_protocol_query(struct filter *, uint64_t, uint64_t, const char *, const char *); - -static void filter_data_internal(struct filter_session *, uint64_t, uint64_t, const char *); -static void filter_data(uint64_t, const char *); -static void filter_data_next(uint64_t, uint64_t, const char *); -static void filter_data_query(struct filter *, uint64_t, uint64_t, const char *); - -static int filter_builtins_notimpl(struct filter_session *, struct filter *, uint64_t, const char *); -static int filter_builtins_connect(struct filter_session *, struct filter *, uint64_t, const char *); -static int filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *); -static int filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *); -static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *); -static int filter_builtins_data(struct filter_session *, struct filter *, uint64_t, const char *); -static int filter_builtins_commit(struct filter_session *, struct filter *, uint64_t, const char *); - -static void filter_result_proceed(uint64_t); -static void filter_result_junk(uint64_t); -static void filter_result_rewrite(uint64_t, const char *); -static void filter_result_reject(uint64_t, const char *); -static void filter_result_disconnect(uint64_t, const char *); - -static void filter_session_io(struct io *, int, void *); -void lka_filter_process_response(const char *, const char *); - - -struct filter_session { - uint64_t id; - struct io *io; - - char *lastparam; - - char *filter_name; - struct sockaddr_storage ss_src; - struct sockaddr_storage ss_dest; - char *rdns; - int fcrdns; - - char *helo; - char *username; - char *mail_from; - - enum filter_phase phase; -}; - -static struct filter_exec { - enum filter_phase phase; - const char *phase_name; - int (*func)(struct filter_session *, struct filter *, uint64_t, const char *); -} filter_execs[FILTER_PHASES_COUNT] = { - { FILTER_CONNECT, "connect", filter_builtins_connect }, - { FILTER_HELO, "helo", filter_builtins_helo }, - { FILTER_EHLO, "ehlo", filter_builtins_helo }, - { FILTER_STARTTLS, "starttls", filter_builtins_notimpl }, - { FILTER_AUTH, "auth", filter_builtins_notimpl }, - { FILTER_MAIL_FROM, "mail-from", filter_builtins_mail_from }, - { FILTER_RCPT_TO, "rcpt-to", filter_builtins_rcpt_to }, - { FILTER_DATA, "data", filter_builtins_data }, - { FILTER_DATA_LINE, "data-line", filter_builtins_notimpl }, - { FILTER_RSET, "rset", filter_builtins_notimpl }, - { FILTER_QUIT, "quit", filter_builtins_notimpl }, - { FILTER_NOOP, "noop", filter_builtins_notimpl }, - { FILTER_HELP, "help", filter_builtins_notimpl }, - { FILTER_WIZ, "wiz", filter_builtins_notimpl }, - { FILTER_COMMIT, "commit", filter_builtins_commit }, -}; - -struct filter { - uint64_t id; - uint32_t phases; - const char *name; - const char *proc; - struct filter **chain; - size_t chain_size; - struct filter_config *config; -}; -static struct dict filters; - -struct filter_entry { - TAILQ_ENTRY(filter_entry) entries; - uint64_t id; - const char *name; -}; - -struct filter_chain { - TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)]; -}; - -static struct dict filter_smtp_in; - -static struct tree sessions; -static int filters_inited; - -static struct dict filter_chains; - -struct reporter_proc { - TAILQ_ENTRY(reporter_proc) entries; - const char *name; -}; -TAILQ_HEAD(reporters, reporter_proc); - -static struct dict report_smtp_in; -static struct dict report_smtp_out; - -static struct smtp_events { - const char *event; -} smtp_events[] = { - { "link-connect" }, - { "link-disconnect" }, - { "link-greeting" }, - { "link-identify" }, - { "link-tls" }, - { "link-auth" }, - - { "tx-reset" }, - { "tx-begin" }, - { "tx-mail" }, - { "tx-rcpt" }, - { "tx-envelope" }, - { "tx-data" }, - { "tx-commit" }, - { "tx-rollback" }, - - { "protocol-client" }, - { "protocol-server" }, - - { "filter-report" }, - { "filter-response" }, - - { "timeout" }, -}; - -static int processors_inited = 0; -static struct dict processors; - -struct processor_instance { - char *name; - struct io *io; - struct io *errfd; - int ready; - uint32_t subsystems; -}; - -static void processor_io(struct io *, int, void *); -static void processor_errfd(struct io *, int, void *); -void lka_filter_process_response(const char *, const char *); - -int -lka_proc_ready(void) -{ - void *iter; - struct processor_instance *pi; - - iter = NULL; - while (dict_iter(&processors, &iter, NULL, (void **)&pi)) - if (!pi->ready) - return 0; - return 1; -} - -static void -lka_proc_config(struct processor_instance *pi) -{ - io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION); - io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT); - if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN) - io_printf(pi->io, "config|subsystem|smtp-in\n"); - if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT) - io_printf(pi->io, "config|subsystem|smtp-out\n"); - io_printf(pi->io, "config|ready\n"); -} - -void -lka_proc_forked(const char *name, uint32_t subsystems, int fd) -{ - struct processor_instance *processor; - - if (!processors_inited) { - dict_init(&processors); - processors_inited = 1; - } - - processor = xcalloc(1, sizeof *processor); - processor->name = xstrdup(name); - processor->io = io_new(); - processor->subsystems = subsystems; - - io_set_nonblocking(fd); - - io_set_fd(processor->io, fd); - io_set_callback(processor->io, processor_io, processor->name); - dict_xset(&processors, name, processor); -} - -void -lka_proc_errfd(const char *name, int fd) -{ - struct processor_instance *processor; - - processor = dict_xget(&processors, name); - - io_set_nonblocking(fd); - - processor->errfd = io_new(); - io_set_fd(processor->errfd, fd); - io_set_callback(processor->errfd, processor_errfd, processor->name); - - lka_proc_config(processor); -} - -struct io * -lka_proc_get_io(const char *name) -{ - struct processor_instance *processor; - - processor = dict_xget(&processors, name); - - return processor->io; -} - -static void -processor_register(const char *name, const char *line) -{ - struct processor_instance *processor; - - processor = dict_xget(&processors, name); - - if (strcmp(line, "register|ready") == 0) { - processor->ready = 1; - return; - } - - if (strncmp(line, "register|report|", 16) == 0) { - lka_report_register_hook(name, line+16); - return; - } - - if (strncmp(line, "register|filter|", 16) == 0) { - lka_filter_register_hook(name, line+16); - return; - } - - fatalx("Invalid register line received: %s", line); -} - -static void -processor_io(struct io *io, int evt, void *arg) -{ - struct processor_instance *processor; - const char *name = arg; - char *line = NULL; - ssize_t len; - - switch (evt) { - case IO_DATAIN: - while ((line = io_getline(io, &len)) != NULL) { - if (strncmp("register|", line, 9) == 0) { - processor_register(name, line); - continue; - } - - processor = dict_xget(&processors, name); - if (!processor->ready) - fatalx("Non-register message before register|" - "ready: %s", line); - else if (strncmp(line, "filter-result|", 14) == 0 || - strncmp(line, "filter-dataline|", 16) == 0) - lka_filter_process_response(name, line); - else if (strncmp(line, "report|", 7) == 0) - lka_report_proc(name, line); - else - fatalx("Invalid filter message type: %s", line); - } - } -} - -static void -processor_errfd(struct io *io, int evt, void *arg) -{ - const char *name = arg; - char *line = NULL; - ssize_t len; - - switch (evt) { - case IO_DATAIN: - while ((line = io_getline(io, &len)) != NULL) - log_warnx("%s: %s", name, line); - } -} - -void -lka_filter_init(void) -{ - void *iter; - const char *name; - struct filter *filter; - struct filter_config *filter_config; - size_t i; - char buffer[LINE_MAX]; /* for traces */ - - dict_init(&filters); - dict_init(&filter_chains); - - /* first pass, allocate and init individual filters */ - iter = NULL; - while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) { - switch (filter_config->filter_type) { - case FILTER_TYPE_BUILTIN: - filter = xcalloc(1, sizeof(*filter)); - filter->name = name; - filter->phases |= (1<<filter_config->phase); - filter->config = filter_config; - dict_set(&filters, name, filter); - log_trace(TRACE_FILTERS, "filters init type=builtin, name=%s, hooks=%08x", - name, filter->phases); - break; - - case FILTER_TYPE_PROC: - filter = xcalloc(1, sizeof(*filter)); - filter->name = name; - filter->proc = filter_config->proc; - filter->config = filter_config; - dict_set(&filters, name, filter); - log_trace(TRACE_FILTERS, "filters init type=proc, name=%s, proc=%s", - name, filter_config->proc); - break; - - case FILTER_TYPE_CHAIN: - break; - } - } - - /* second pass, allocate and init filter chains but don't build yet */ - iter = NULL; - while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) { - switch (filter_config->filter_type) { - case FILTER_TYPE_CHAIN: - filter = xcalloc(1, sizeof(*filter)); - filter->name = name; - filter->chain = xcalloc(filter_config->chain_size, sizeof(void **)); - filter->chain_size = filter_config->chain_size; - filter->config = filter_config; - - buffer[0] = '\0'; - for (i = 0; i < filter->chain_size; ++i) { - filter->chain[i] = dict_xget(&filters, filter_config->chain[i]); - if (i) - (void)strlcat(buffer, ", ", sizeof buffer); - (void)strlcat(buffer, filter->chain[i]->name, sizeof buffer); - } - log_trace(TRACE_FILTERS, "filters init type=chain, name=%s { %s }", name, buffer); - - dict_set(&filters, name, filter); - break; - - case FILTER_TYPE_BUILTIN: - case FILTER_TYPE_PROC: - break; - } - } -} - -void -lka_filter_register_hook(const char *name, const char *hook) -{ - struct dict *subsystem; - struct filter *filter; - const char *filter_name; - void *iter; - size_t i; - - if (strncasecmp(hook, "smtp-in|", 8) == 0) { - subsystem = &filter_smtp_in; - hook += 8; - } - else - fatalx("Invalid message direction: %s", hook); - - for (i = 0; i < nitems(filter_execs); i++) - if (strcmp(hook, filter_execs[i].phase_name) == 0) - break; - if (i == nitems(filter_execs)) - fatalx("Unrecognized report name: %s", hook); - - iter = NULL; - while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) - if (filter->proc && strcmp(name, filter->proc) == 0) - filter->phases |= (1<<filter_execs[i].phase); -} - -void -lka_filter_ready(void) -{ - struct filter *filter; - struct filter *subfilter; - const char *filter_name; - struct filter_entry *filter_entry; - struct filter_chain *filter_chain; - void *iter; - size_t i; - size_t j; - - /* all filters are ready, actually build the filter chains */ - iter = NULL; - while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) { - filter_chain = xcalloc(1, sizeof *filter_chain); - for (i = 0; i < nitems(filter_execs); i++) - TAILQ_INIT(&filter_chain->chain[i]); - dict_set(&filter_chains, filter_name, filter_chain); - - if (filter->chain) { - for (i = 0; i < filter->chain_size; i++) { - subfilter = filter->chain[i]; - for (j = 0; j < nitems(filter_execs); ++j) { - if (subfilter->phases & (1<<j)) { - filter_entry = xcalloc(1, sizeof *filter_entry); - filter_entry->id = generate_uid(); - filter_entry->name = subfilter->name; - TAILQ_INSERT_TAIL(&filter_chain->chain[j], - filter_entry, entries); - } - } - } - continue; - } - - for (i = 0; i < nitems(filter_execs); ++i) { - if (filter->phases & (1<<i)) { - filter_entry = xcalloc(1, sizeof *filter_entry); - filter_entry->id = generate_uid(); - filter_entry->name = filter_name; - TAILQ_INSERT_TAIL(&filter_chain->chain[i], - filter_entry, entries); - } - } - } -} - -int -lka_filter_proc_in_session(uint64_t reqid, const char *proc) -{ - struct filter_session *fs; - struct filter *filter; - size_t i; - - if ((fs = tree_get(&sessions, reqid)) == NULL) - return 0; - - filter = dict_get(&filters, fs->filter_name); - if (filter == NULL || (filter->proc == NULL && filter->chain == NULL)) - return 0; - - if (filter->proc) - return strcmp(filter->proc, proc) == 0 ? 1 : 0; - - for (i = 0; i < filter->chain_size; i++) - if (filter->chain[i]->proc && - strcmp(filter->chain[i]->proc, proc) == 0) - return 1; - - return 0; -} - -void -lka_filter_begin(uint64_t reqid, const char *filter_name) -{ - struct filter_session *fs; - - if (!filters_inited) { - tree_init(&sessions); - filters_inited = 1; - } - - fs = xcalloc(1, sizeof (struct filter_session)); - fs->id = reqid; - fs->filter_name = xstrdup(filter_name); - tree_xset(&sessions, fs->id, fs); - - log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid); -} - -void -lka_filter_end(uint64_t reqid) -{ - struct filter_session *fs; - - fs = tree_xpop(&sessions, reqid); - free(fs->rdns); - free(fs->helo); - free(fs->mail_from); - free(fs->username); - free(fs->lastparam); - free(fs); - log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-end", reqid); -} - -void -lka_filter_data_begin(uint64_t reqid) -{ - struct filter_session *fs; - int sp[2]; - int fd = -1; - - fs = tree_xget(&sessions, reqid); - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) - goto end; - io_set_nonblocking(sp[0]); - io_set_nonblocking(sp[1]); - fd = sp[0]; - fs->io = io_new(); - io_set_fd(fs->io, sp[1]); - io_set_callback(fs->io, filter_session_io, fs); - -end: - m_create(p_pony, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, fd); - m_add_id(p_pony, reqid); - m_add_int(p_pony, fd != -1 ? 1 : 0); - m_close(p_pony); - log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-begin fd=%d", reqid, fd); -} - -void -lka_filter_data_end(uint64_t reqid) -{ - struct filter_session *fs; - - fs = tree_xget(&sessions, reqid); - if (fs->io) { - io_free(fs->io); - fs->io = NULL; - } - log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-end", reqid); -} - -static void -filter_session_io(struct io *io, int evt, void *arg) -{ - struct filter_session *fs = arg; - char *line = NULL; - ssize_t len; - - log_trace(TRACE_IO, "filter session: %p: %s %s", fs, io_strevent(evt), - io_strio(io)); - - switch (evt) { - case IO_DATAIN: - nextline: - line = io_getline(fs->io, &len); - /* No complete line received */ - if (line == NULL) - return; - - filter_data(fs->id, line); - - goto nextline; - - case IO_DISCONNECTED: - io_free(fs->io); - fs->io = NULL; - break; - } -} - -void -lka_filter_process_response(const char *name, const char *line) -{ - uint64_t reqid; - uint64_t token; - char buffer[LINE_MAX]; - char *ep = NULL; - char *kind = NULL; - char *qid = NULL; - /*char *phase = NULL;*/ - char *response = NULL; - char *parameter = NULL; - struct filter_session *fs; - - (void)strlcpy(buffer, line, sizeof buffer); - if ((ep = strchr(buffer, '|')) == NULL) - fatalx("Missing token: %s", line); - ep[0] = '\0'; - - kind = buffer; - - qid = ep+1; - if ((ep = strchr(qid, '|')) == NULL) - fatalx("Missing reqid: %s", line); - ep[0] = '\0'; - - reqid = strtoull(qid, &ep, 16); - if (qid[0] == '\0' || *ep != '\0') - fatalx("Invalid reqid: %s", line); - if (errno == ERANGE && reqid == ULLONG_MAX) - fatal("Invalid reqid: %s", line); - - qid = ep+1; - if ((ep = strchr(qid, '|')) == NULL) - fatal("Missing directive: %s", line); - ep[0] = '\0'; - - token = strtoull(qid, &ep, 16); - if (qid[0] == '\0' || *ep != '\0') - fatalx("Invalid token: %s", line); - if (errno == ERANGE && token == ULLONG_MAX) - fatal("Invalid token: %s", line); - - response = ep+1; - - /* session can legitimately disappear on a resume */ - if ((fs = tree_get(&sessions, reqid)) == NULL) - return; - - if (strcmp(kind, "filter-dataline") == 0) { - if (fs->phase != FILTER_DATA_LINE) - fatalx("filter-dataline out of dataline phase"); - filter_data_next(token, reqid, response); - return; - } - if (fs->phase == FILTER_DATA_LINE) - fatalx("filter-result in dataline phase"); - - if ((ep = strchr(response, '|'))) { - parameter = ep + 1; - ep[0] = '\0'; - } - - if (strcmp(response, "proceed") == 0) { - if (parameter != NULL) - fatalx("Unexpected parameter after proceed: %s", line); - filter_protocol_next(token, reqid, 0); - return; - } else if (strcmp(response, "junk") == 0) { - if (parameter != NULL) - fatalx("Unexpected parameter after junk: %s", line); - if (fs->phase == FILTER_COMMIT) - fatalx("filter-reponse junk after DATA"); - filter_result_junk(reqid); - return; - } else { - if (parameter == NULL) - fatalx("Missing parameter: %s", line); - - if (strcmp(response, "rewrite") == 0) - filter_result_rewrite(reqid, parameter); - else if (strcmp(response, "reject") == 0) - filter_result_reject(reqid, parameter); - else if (strcmp(response, "disconnect") == 0) - filter_result_disconnect(reqid, parameter); - else - fatalx("Invalid directive: %s", line); - } -} - -void -lka_filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param) -{ - filter_protocol(reqid, phase, param); -} - -static void -filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t reqid, enum filter_phase phase, const char *param) -{ - struct filter_chain *filter_chain; - struct filter_entry *filter_entry; - struct filter *filter; - struct timeval tv; - const char *phase_name = filter_execs[phase].phase_name; - int resume = 1; - - if (!*token) { - fs->phase = phase; - resume = 0; - } - - /* XXX - this sanity check requires a protocol change, stub for now */ - phase = fs->phase; - if (fs->phase != phase) - fatalx("misbehaving filter"); - - /* based on token, identify the filter_entry we should apply */ - filter_chain = dict_get(&filter_chains, fs->filter_name); - filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]); - if (*token) { - TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries) - if (filter_entry->id == *token) - break; - if (filter_entry == NULL) - fatalx("misbehaving filter"); - filter_entry = TAILQ_NEXT(filter_entry, entries); - } - - /* no filter_entry, we either had none or reached end of chain */ - if (filter_entry == NULL) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, resume=%s, " - "action=proceed", - fs->id, phase_name, resume ? "y" : "n"); - filter_result_proceed(reqid); - return; - } - - /* process param with current filter_entry */ - *token = filter_entry->id; - filter = dict_get(&filters, filter_entry->name); - if (filter->proc) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=deferred, filter=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name); - filter_protocol_query(filter, filter_entry->id, reqid, - filter_execs[fs->phase].phase_name, param); - return; /* deferred response */ - } - - if (filter_execs[fs->phase].func(fs, filter, reqid, param)) { - if (filter->config->rewrite) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=rewrite, filter=%s, query=%s, response=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param, - filter->config->rewrite); - filter_result_rewrite(reqid, filter->config->rewrite); - return; - } - else if (filter->config->disconnect) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=disconnect, filter=%s, query=%s, response=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param, - filter->config->disconnect); - filter_result_disconnect(reqid, filter->config->disconnect); - return; - } - else if (filter->config->junk) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=junk, filter=%s, query=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param); - filter_result_junk(reqid); - return; - } else if (filter->config->report) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=report, filter=%s, query=%s response=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param, filter->config->report); - - gettimeofday(&tv, NULL); - lka_report_filter_report(fs->id, filter->name, 1, - "smtp-in", &tv, filter->config->report); - } else if (filter->config->bypass) { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=bypass, filter=%s, query=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param); - filter_result_proceed(reqid); - return; - } else { - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=reject, filter=%s, query=%s, response=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param, - filter->config->reject); - filter_result_reject(reqid, filter->config->reject); - return; - } - } - - log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " - "resume=%s, action=proceed, filter=%s, query=%s", - fs->id, phase_name, resume ? "y" : "n", - filter->name, - param); - - /* filter_entry resulted in proceed, try next filter */ - filter_protocol_internal(fs, token, reqid, phase, param); - return; -} - -static void -filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid, const char *line) -{ - struct filter_chain *filter_chain; - struct filter_entry *filter_entry; - struct filter *filter; - - if (!token) - fs->phase = FILTER_DATA_LINE; - if (fs->phase != FILTER_DATA_LINE) - fatalx("misbehaving filter"); - - /* based on token, identify the filter_entry we should apply */ - filter_chain = dict_get(&filter_chains, fs->filter_name); - filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]); - if (token) { - TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries) - if (filter_entry->id == token) - break; - if (filter_entry == NULL) - fatalx("misbehaving filter"); - filter_entry = TAILQ_NEXT(filter_entry, entries); - } - - /* no filter_entry, we either had none or reached end of chain */ - if (filter_entry == NULL) { - io_printf(fs->io, "%s\n", line); - return; - } - - /* pass data to the filter */ - filter = dict_get(&filters, filter_entry->name); - filter_data_query(filter, filter_entry->id, reqid, line); -} - -static void -filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param) -{ - struct filter_session *fs; - uint64_t token = 0; - char *nparam = NULL; - - fs = tree_xget(&sessions, reqid); - - switch (phase) { - case FILTER_HELO: - case FILTER_EHLO: - free(fs->helo); - fs->helo = xstrdup(param); - break; - case FILTER_MAIL_FROM: - free(fs->mail_from); - fs->mail_from = xstrdup(param + 1); - *strchr(fs->mail_from, '>') = '\0'; - param = fs->mail_from; - - break; - case FILTER_RCPT_TO: - nparam = xstrdup(param + 1); - *strchr(nparam, '>') = '\0'; - param = nparam; - break; - case FILTER_STARTTLS: - /* TBD */ - break; - default: - break; - } - - free(fs->lastparam); - fs->lastparam = xstrdup(param); - - filter_protocol_internal(fs, &token, reqid, phase, param); - if (nparam) - free(nparam); -} - -static void -filter_protocol_next(uint64_t token, uint64_t reqid, enum filter_phase phase) -{ - struct filter_session *fs; - - /* session can legitimately disappear on a resume */ - if ((fs = tree_get(&sessions, reqid)) == NULL) - return; - - filter_protocol_internal(fs, &token, reqid, phase, fs->lastparam); -} - -static void -filter_data(uint64_t reqid, const char *line) -{ - struct filter_session *fs; - - fs = tree_xget(&sessions, reqid); - - filter_data_internal(fs, 0, reqid, line); -} - -static void -filter_data_next(uint64_t token, uint64_t reqid, const char *line) -{ - struct filter_session *fs; - - /* session can legitimately disappear on a resume */ - if ((fs = tree_get(&sessions, reqid)) == NULL) - return; - - filter_data_internal(fs, token, reqid, line); -} - -static void -filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *phase, const char *param) -{ - int n; - struct filter_session *fs; - struct timeval tv; - - gettimeofday(&tv, NULL); - - fs = tree_xget(&sessions, reqid); - if (strcmp(phase, "connect") == 0) - n = io_printf(lka_proc_get_io(filter->proc), - "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n", - PROTOCOL_VERSION, - (long long int)tv.tv_sec, tv.tv_usec, - phase, reqid, token, fs->rdns, param); - else - n = io_printf(lka_proc_get_io(filter->proc), - "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n", - PROTOCOL_VERSION, - (long long int)tv.tv_sec, tv.tv_usec, - phase, reqid, token, param); - if (n == -1) - fatalx("failed to write to processor"); -} - -static void -filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *line) -{ - int n; - struct timeval tv; - - gettimeofday(&tv, NULL); - - n = io_printf(lka_proc_get_io(filter->proc), - "filter|%s|%lld.%06ld|smtp-in|data-line|" - "%016"PRIx64"|%016"PRIx64"|%s\n", - PROTOCOL_VERSION, - (long long int)tv.tv_sec, tv.tv_usec, - reqid, token, line); - if (n == -1) - fatalx("failed to write to processor"); -} - -static void -filter_result_proceed(uint64_t reqid) -{ - m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); - m_add_id(p_pony, reqid); - m_add_int(p_pony, FILTER_PROCEED); - m_close(p_pony); -} - -static void -filter_result_junk(uint64_t reqid) -{ - m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); - m_add_id(p_pony, reqid); - m_add_int(p_pony, FILTER_JUNK); - m_close(p_pony); -} - -static void -filter_result_rewrite(uint64_t reqid, const char *param) -{ - m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); - m_add_id(p_pony, reqid); - m_add_int(p_pony, FILTER_REWRITE); - m_add_string(p_pony, param); - m_close(p_pony); -} - -static void -filter_result_reject(uint64_t reqid, const char *message) -{ - m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); - m_add_id(p_pony, reqid); - m_add_int(p_pony, FILTER_REJECT); - m_add_string(p_pony, message); - m_close(p_pony); -} - -static void -filter_result_disconnect(uint64_t reqid, const char *message) -{ - m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); - m_add_id(p_pony, reqid); - m_add_int(p_pony, FILTER_DISCONNECT); - m_add_string(p_pony, message); - m_close(p_pony); -} - - -/* below is code for builtin filters */ - -static int -filter_check_rdns_table(struct filter *filter, enum table_service kind, const char *key) -{ - int ret = 0; - - if (filter->config->rdns_table == NULL) - return 0; - - if (table_match(filter->config->rdns_table, kind, key) > 0) - ret = 1; - - return filter->config->not_rdns_table < 0 ? !ret : ret; -} - -static int -filter_check_rdns_regex(struct filter *filter, const char *key) -{ - int ret = 0; - - if (filter->config->rdns_regex == NULL) - return 0; - - if (table_match(filter->config->rdns_regex, K_REGEX, key) > 0) - ret = 1; - return filter->config->not_rdns_regex < 0 ? !ret : ret; -} - -static int -filter_check_src_table(struct filter *filter, enum table_service kind, const char *key) -{ - int ret = 0; - - if (filter->config->src_table == NULL) - return 0; - - if (table_match(filter->config->src_table, kind, key) > 0) - ret = 1; - return filter->config->not_src_table < 0 ? !ret : ret; -} - -static int -filter_check_src_regex(struct filter *filter, const char *key) -{ - int ret = 0; - - if (filter->config->src_regex == NULL) - return 0; - - if (table_match(filter->config->src_regex, K_REGEX, key) > 0) - ret = 1; - return filter->config->not_src_regex < 0 ? !ret : ret; -} - -static int -filter_check_helo_table(struct filter *filter, enum table_service kind, const char *key) -{ - int ret = 0; - - if (filter->config->helo_table == NULL) - return 0; - - if (table_match(filter->config->helo_table, kind, key) > 0) - ret = 1; - return filter->config->not_helo_table < 0 ? !ret : ret; -} - -static int -filter_check_helo_regex(struct filter *filter, const char *key) -{ - int ret = 0; - - if (filter->config->helo_regex == NULL) - return 0; - - if (table_match(filter->config->helo_regex, K_REGEX, key) > 0) - ret = 1; - return filter->config->not_helo_regex < 0 ? !ret : ret; -} - -static int -filter_check_auth(struct filter *filter, const char *username) -{ - int ret = 0; - - if (!filter->config->auth) - return 0; - - ret = username ? 1 : 0; - - return filter->config->not_auth < 0 ? !ret : ret; -} - -static int -filter_check_auth_table(struct filter *filter, enum table_service kind, const char *key) -{ - int ret = 0; - - if (filter->config->auth_table == NULL) - return 0; - - if (key && table_match(filter->config->auth_table, kind, key) > 0) - ret = 1; - - return filter->config->not_auth_table < 0 ? !ret : ret; -} - -static int -filter_check_auth_regex(struct filter *filter, const char *key) -{ - int ret = 0; - - if (filter->config->auth_regex == NULL) - return 0; - - if (key && table_match(filter->config->auth_regex, K_REGEX, key) > 0) - ret = 1; - return filter->config->not_auth_regex < 0 ? !ret : ret; -} - - -static int -filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key) -{ - int ret = 0; - - if (filter->config->mail_from_table == NULL) - return 0; - - if (table_match(filter->config->mail_from_table, kind, key) > 0) - ret = 1; - return filter->config->not_mail_from_table < 0 ? !ret : ret; -} - -static int -filter_check_mail_from_regex(struct filter *filter, const char *key) -{ - int ret = 0; - - if (filter->config->mail_from_regex == NULL) - return 0; - - if (table_match(filter->config->mail_from_regex, K_REGEX, key) > 0) - ret = 1; - return filter->config->not_mail_from_regex < 0 ? !ret : ret; -} - -static int -filter_check_rcpt_to_table(struct filter *filter, enum table_service kind, const char *key) -{ - int ret = 0; - - if (filter->config->rcpt_to_table == NULL) - return 0; - - if (table_match(filter->config->rcpt_to_table, kind, key) > 0) - ret = 1; - return filter->config->not_rcpt_to_table < 0 ? !ret : ret; -} - -static int -filter_check_rcpt_to_regex(struct filter *filter, const char *key) -{ - int ret = 0; - - if (filter->config->rcpt_to_regex == NULL) - return 0; - - if (table_match(filter->config->rcpt_to_regex, K_REGEX, key) > 0) - ret = 1; - return filter->config->not_rcpt_to_regex < 0 ? !ret : ret; -} - -static int -filter_check_fcrdns(struct filter *filter, int fcrdns) -{ - int ret = 0; - - if (!filter->config->fcrdns) - return 0; - - ret = fcrdns == 1; - return filter->config->not_fcrdns < 0 ? !ret : ret; -} - -static int -filter_check_rdns(struct filter *filter, const char *hostname) -{ - int ret = 0; - struct netaddr netaddr; - - if (!filter->config->rdns) - return 0; - - /* this is a hack until smtp session properly deals with lack of rdns */ - ret = strcmp("<unknown>", hostname); - if (ret == 0) - return filter->config->not_rdns < 0 ? !ret : ret; - - /* if text_to_netaddress succeeds, - * we don't have an rDNS so the filter should match - */ - ret = !text_to_netaddr(&netaddr, hostname); - return filter->config->not_rdns < 0 ? !ret : ret; -} - -static int -filter_builtins_notimpl(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return 0; -} - -static int -filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_t reqid) -{ - return filter_check_fcrdns(filter, fs->fcrdns) || - filter_check_rdns(filter, fs->rdns) || - filter_check_rdns_table(filter, K_DOMAIN, fs->rdns) || - filter_check_rdns_regex(filter, fs->rdns) || - filter_check_src_table(filter, K_NETADDR, ss_to_text(&fs->ss_src)) || - filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) || - filter_check_helo_table(filter, K_DOMAIN, fs->helo) || - filter_check_helo_regex(filter, fs->helo) || - filter_check_auth(filter, fs->username) || - filter_check_auth_table(filter, K_STRING, fs->username) || - filter_check_auth_table(filter, K_CREDENTIALS, fs->username) || - filter_check_auth_regex(filter, fs->username) || - filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) || - filter_check_mail_from_regex(filter, fs->mail_from); -} - -static int -filter_builtins_connect(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return filter_builtins_global(fs, filter, reqid); -} - -static int -filter_builtins_helo(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return filter_builtins_global(fs, filter, reqid); -} - -static int -filter_builtins_mail_from(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return filter_builtins_global(fs, filter, reqid); -} - -static int -filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return filter_builtins_global(fs, filter, reqid) || - filter_check_rcpt_to_table(filter, K_MAILADDR, param) || - filter_check_rcpt_to_regex(filter, param); -} - -static int -filter_builtins_data(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return filter_builtins_global(fs, filter, reqid); -} - -static int -filter_builtins_commit(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) -{ - return filter_builtins_global(fs, filter, reqid); -} - -static void -report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *, - const char *, ...) __attribute__((__format__ (printf, 5, 6))); - -void -lka_report_init(void) -{ - struct reporters *tailq; - size_t i; - - dict_init(&report_smtp_in); - dict_init(&report_smtp_out); - - for (i = 0; i < nitems(smtp_events); ++i) { - tailq = xcalloc(1, sizeof (struct reporters)); - TAILQ_INIT(tailq); - dict_xset(&report_smtp_in, smtp_events[i].event, tailq); - - tailq = xcalloc(1, sizeof (struct reporters)); - TAILQ_INIT(tailq); - dict_xset(&report_smtp_out, smtp_events[i].event, tailq); - } -} - -void -lka_report_register_hook(const char *name, const char *hook) -{ - struct dict *subsystem; - struct reporter_proc *rp; - struct reporters *tailq; - void *iter; - size_t i; - - if (strncmp(hook, "smtp-in|", 8) == 0) { - subsystem = &report_smtp_in; - hook += 8; - } - else if (strncmp(hook, "smtp-out|", 9) == 0) { - subsystem = &report_smtp_out; - hook += 9; - } - else - fatalx("Invalid message direction: %s", hook); - - if (strcmp(hook, "*") == 0) { - iter = NULL; - while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) { - rp = xcalloc(1, sizeof *rp); - rp->name = xstrdup(name); - TAILQ_INSERT_TAIL(tailq, rp, entries); - } - return; - } - - for (i = 0; i < nitems(smtp_events); i++) - if (strcmp(hook, smtp_events[i].event) == 0) - break; - if (i == nitems(smtp_events)) - fatalx("Unrecognized report name: %s", hook); - - tailq = dict_get(subsystem, hook); - rp = xcalloc(1, sizeof *rp); - rp->name = xstrdup(name); - TAILQ_INSERT_TAIL(tailq, rp, entries); -} - -static void -report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event, - const char *format, ...) -{ - va_list ap; - struct dict *d; - struct reporters *tailq; - struct reporter_proc *rp; - - if (strcmp("smtp-in", direction) == 0) - d = &report_smtp_in; - - else if (strcmp("smtp-out", direction) == 0) - d = &report_smtp_out; - - else - fatalx("unexpected direction: %s", direction); - - tailq = dict_xget(d, event); - TAILQ_FOREACH(rp, tailq, entries) { - if (!lka_filter_proc_in_session(reqid, rp->name)) - continue; - - va_start(ap, format); - if (io_printf(lka_proc_get_io(rp->name), - "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s", - PROTOCOL_VERSION, (long long int)tv->tv_sec, tv->tv_usec, direction, - event, reqid, format[0] != '\n' ? "|" : "") == -1 || - io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1) - fatalx("failed to write to processor"); - va_end(ap); - } -} - -void -lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns, - int fcrdns, - const struct sockaddr_storage *ss_src, - const struct sockaddr_storage *ss_dest) -{ - struct filter_session *fs; - char src[NI_MAXHOST + 5]; - char dest[NI_MAXHOST + 5]; - uint16_t src_port = 0; - uint16_t dest_port = 0; - const char *fcrdns_str; - - if (ss_src->ss_family == AF_INET) - src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port); - else if (ss_src->ss_family == AF_INET6) - src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port); - - if (ss_dest->ss_family == AF_INET) - dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port); - else if (ss_dest->ss_family == AF_INET6) - dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port); - - if (strcmp(ss_to_text(ss_src), "local") == 0) { - (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET); - (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET); - } else { - (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port); - (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port); - } - - switch (fcrdns) { - case 1: - fcrdns_str = "pass"; - break; - case 0: - fcrdns_str = "fail"; - break; - default: - fcrdns_str = "error"; - break; - } - - fs = tree_xget(&sessions, reqid); - fs->rdns = xstrdup(rdns); - fs->fcrdns = fcrdns; - fs->ss_src = *ss_src; - fs->ss_dest = *ss_dest; - - report_smtp_broadcast(reqid, direction, tv, "link-connect", - "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest); -} - -void -lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid) -{ - report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n"); -} - -void -lka_report_smtp_link_greeting(const char *direction, uint64_t reqid, - struct timeval *tv, const char *domain) -{ - report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n", - domain); -} - -void -lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid, - const char *username, const char *result) -{ - struct filter_session *fs; - - if (strcmp(result, "pass") == 0) { - fs = tree_xget(&sessions, reqid); - fs->username = xstrdup(username); - } - report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n", - username, result); -} - -void -lka_report_smtp_link_identify(const char *direction, struct timeval *tv, - uint64_t reqid, const char *method, const char *heloname) -{ - report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n", - method, heloname); -} - -void -lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers) -{ - report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n", - ciphers); -} - -void -lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n", - msgid); -} - -void -lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n", - msgid); -} - -void -lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) -{ - const char *result; - - switch (ok) { - case 1: - result = "ok"; - break; - case 0: - result = "permfail"; - break; - default: - result = "tempfail"; - break; - } - report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n", - msgid, result, address); -} - -void -lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) -{ - const char *result; - - switch (ok) { - case 1: - result = "ok"; - break; - case 0: - result = "permfail"; - break; - default: - result = "tempfail"; - break; - } - report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n", - msgid, result, address); -} - -void -lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-envelope", - "%08x|%016"PRIx64"\n", msgid, evpid); -} - -void -lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok) -{ - const char *result; - - switch (ok) { - case 1: - result = "ok"; - break; - case 0: - result = "permfail"; - break; - default: - result = "tempfail"; - break; - } - report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n", - msgid, result); -} - -void -lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n", - msgid, msgsz); -} - -void -lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n", - msgid); -} - -void -lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command) -{ - report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n", - command); -} - -void -lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response) -{ - report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n", - response); -} - -void -lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid, - int phase, int response, const char *param) -{ - const char *phase_name; - const char *response_name; - - switch (phase) { - case FILTER_CONNECT: - phase_name = "connected"; - break; - case FILTER_HELO: - phase_name = "helo"; - break; - case FILTER_EHLO: - phase_name = "ehlo"; - break; - case FILTER_STARTTLS: - phase_name = "tls"; - break; - case FILTER_AUTH: - phase_name = "auth"; - break; - case FILTER_MAIL_FROM: - phase_name = "mail-from"; - break; - case FILTER_RCPT_TO: - phase_name = "rcpt-to"; - break; - case FILTER_DATA: - phase_name = "data"; - break; - case FILTER_DATA_LINE: - phase_name = "data-line"; - break; - case FILTER_RSET: - phase_name = "rset"; - break; - case FILTER_QUIT: - phase_name = "quit"; - break; - case FILTER_NOOP: - phase_name = "noop"; - break; - case FILTER_HELP: - phase_name = "help"; - break; - case FILTER_WIZ: - phase_name = "wiz"; - break; - case FILTER_COMMIT: - phase_name = "commit"; - break; - default: - phase_name = ""; - } - - switch (response) { - case FILTER_PROCEED: - response_name = "proceed"; - break; - case FILTER_JUNK: - response_name = "junk"; - break; - case FILTER_REWRITE: - response_name = "rewrite"; - break; - case FILTER_REJECT: - response_name = "reject"; - break; - case FILTER_DISCONNECT: - response_name = "disconnect"; - break; - default: - response_name = ""; - } - - report_smtp_broadcast(reqid, direction, tv, "filter-response", - "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "", - param ? param : ""); -} - -void -lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid) -{ - report_smtp_broadcast(reqid, direction, tv, "timeout", "\n"); -} - -void -lka_report_filter_report(uint64_t reqid, const char *name, int builtin, - const char *direction, struct timeval *tv, const char *message) -{ - report_smtp_broadcast(reqid, direction, tv, "filter-report", - "%s|%s|%s\n", builtin ? "builtin" : "proc", - name, message); -} - -void -lka_report_proc(const char *name, const char *line) -{ - char buffer[LINE_MAX]; - struct timeval tv; - char *ep, *sp, *direction; - uint64_t reqid; - - if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer)) - fatalx("Invalid report: line too long: %s", line); - - errno = 0; - tv.tv_sec = strtoll(buffer, &ep, 10); - if (ep[0] != '.' || errno != 0) - fatalx("Invalid report: invalid time: %s", line); - sp = ep + 1; - tv.tv_usec = strtol(sp, &ep, 10); - if (ep[0] != '|' || errno != 0) - fatalx("Invalid report: invalid time: %s", line); - if (ep - sp != 6) - fatalx("Invalid report: invalid time: %s", line); - - direction = ep + 1; - if (strncmp(direction, "smtp-in|", 8) == 0) { - direction[7] = '\0'; - direction += 7; -#if 0 - } else if (strncmp(direction, "smtp-out|", 9) == 0) { - direction[8] = '\0'; - direction += 8; -#endif - } else - fatalx("Invalid report: invalid direction: %s", line); - - reqid = strtoull(sp, &ep, 16); - if (ep[0] != '|' || errno != 0) - fatalx("Invalid report: invalid reqid: %s", line); - sp = ep + 1; - - lka_report_filter_report(reqid, name, 0, direction, &tv, sp); -} diff --git a/smtpd/lka_session.c b/smtpd/lka_session.c deleted file mode 100644 index 999e01d6..00000000 --- a/smtpd/lka_session.c +++ /dev/null @@ -1,556 +0,0 @@ -/* $OpenBSD: lka_session.c,v 1.93 2019/09/20 17:46:05 gilles Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/wait.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <resolv.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define EXPAND_DEPTH 10 - -#define F_WAITING 0x01 - -struct lka_session { - uint64_t id; /* given by smtp */ - - TAILQ_HEAD(, envelope) deliverylist; - struct expand expand; - - int flags; - int error; - const char *errormsg; - struct envelope envelope; - struct xnodes nodes; - /* waiting for fwdrq */ - struct rule *rule; - struct expandnode *node; -}; - -static void lka_expand(struct lka_session *, struct rule *, - struct expandnode *); -static void lka_submit(struct lka_session *, struct rule *, - struct expandnode *); -static void lka_resume(struct lka_session *); - -static int init; -static struct tree sessions; - -void -lka_session(uint64_t id, struct envelope *envelope) -{ - struct lka_session *lks; - struct expandnode xn; - - if (init == 0) { - init = 1; - tree_init(&sessions); - } - - lks = xcalloc(1, sizeof(*lks)); - lks->id = id; - RB_INIT(&lks->expand.tree); - TAILQ_INIT(&lks->deliverylist); - tree_xset(&sessions, lks->id, lks); - - lks->envelope = *envelope; - - TAILQ_INIT(&lks->nodes); - memset(&xn, 0, sizeof xn); - xn.type = EXPAND_ADDRESS; - xn.u.mailaddr = lks->envelope.rcpt; - lks->expand.parent = NULL; - lks->expand.rule = NULL; - lks->expand.queue = &lks->nodes; - expand_insert(&lks->expand, &xn); - lka_resume(lks); -} - -void -lka_session_forward_reply(struct forward_req *fwreq, int fd) -{ - struct lka_session *lks; - struct dispatcher *dsp; - struct rule *rule; - struct expandnode *xn; - int ret; - - lks = tree_xget(&sessions, fwreq->id); - xn = lks->node; - rule = lks->rule; - - lks->flags &= ~F_WAITING; - - switch (fwreq->status) { - case 0: - /* permanent failure while lookup ~/.forward */ - log_trace(TRACE_EXPAND, "expand: ~/.forward failed for user %s", - fwreq->user); - lks->error = LKA_PERMFAIL; - break; - case 1: - if (fd == -1) { - dsp = dict_get(env->sc_dispatchers, lks->rule->dispatcher); - if (dsp->u.local.forward_only) { - log_trace(TRACE_EXPAND, "expand: no .forward " - "for user %s on forward-only rule", fwreq->user); - lks->error = LKA_TEMPFAIL; - } - else if (dsp->u.local.expand_only) { - log_trace(TRACE_EXPAND, "expand: no .forward " - "for user %s and no default action on rule", fwreq->user); - lks->error = LKA_PERMFAIL; - } - else { - log_trace(TRACE_EXPAND, "expand: no .forward for " - "user %s, just deliver", fwreq->user); - lka_submit(lks, rule, xn); - } - } - else { - dsp = dict_get(env->sc_dispatchers, rule->dispatcher); - - /* expand for the current user and rule */ - lks->expand.rule = rule; - lks->expand.parent = xn; - - /* forwards_get() will close the descriptor no matter what */ - ret = forwards_get(fd, &lks->expand); - if (ret == -1) { - log_trace(TRACE_EXPAND, "expand: temporary " - "forward error for user %s", fwreq->user); - lks->error = LKA_TEMPFAIL; - } - else if (ret == 0) { - if (dsp->u.local.forward_only) { - log_trace(TRACE_EXPAND, "expand: empty .forward " - "for user %s on forward-only rule", fwreq->user); - lks->error = LKA_TEMPFAIL; - } - else if (dsp->u.local.expand_only) { - log_trace(TRACE_EXPAND, "expand: empty .forward " - "for user %s and no default action on rule", fwreq->user); - lks->error = LKA_PERMFAIL; - } - else { - log_trace(TRACE_EXPAND, "expand: empty .forward " - "for user %s, just deliver", fwreq->user); - lka_submit(lks, rule, xn); - } - } - } - break; - default: - /* temporary failure while looking up ~/.forward */ - lks->error = LKA_TEMPFAIL; - } - - if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL) - lks->errormsg = "424 4.2.4 Mailing list expansion problem"; - if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL) - lks->errormsg = "524 5.2.4 Mailing list expansion problem"; - - lka_resume(lks); -} - -static void -lka_resume(struct lka_session *lks) -{ - struct envelope *ep; - struct expandnode *xn; - - if (lks->error) - goto error; - - /* pop next node and expand it */ - while ((xn = TAILQ_FIRST(&lks->nodes))) { - TAILQ_REMOVE(&lks->nodes, xn, tq_entry); - lka_expand(lks, xn->rule, xn); - if (lks->flags & F_WAITING) - return; - if (lks->error) - goto error; - } - - /* delivery list is empty, reject */ - if (TAILQ_FIRST(&lks->deliverylist) == NULL) { - log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty " - "delivery list"); - lks->error = LKA_PERMFAIL; - lks->errormsg = "524 5.2.4 Mailing list expansion problem"; - } - error: - if (lks->error) { - m_create(p_pony, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1); - m_add_id(p_pony, lks->id); - m_add_int(p_pony, lks->error); - - if (lks->errormsg) - m_add_string(p_pony, lks->errormsg); - else { - if (lks->error == LKA_PERMFAIL) - m_add_string(p_pony, "550 Invalid recipient"); - else if (lks->error == LKA_TEMPFAIL) - m_add_string(p_pony, "451 Temporary failure"); - } - - m_close(p_pony); - while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { - TAILQ_REMOVE(&lks->deliverylist, ep, entry); - free(ep); - } - } - else { - /* Process the delivery list and submit envelopes to queue */ - while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { - TAILQ_REMOVE(&lks->deliverylist, ep, entry); - m_create(p_queue, IMSG_LKA_ENVELOPE_SUBMIT, 0, 0, -1); - m_add_id(p_queue, lks->id); - m_add_envelope(p_queue, ep); - m_close(p_queue); - free(ep); - } - - m_create(p_queue, IMSG_LKA_ENVELOPE_COMMIT, 0, 0, -1); - m_add_id(p_queue, lks->id); - m_close(p_queue); - } - - expand_clear(&lks->expand); - tree_xpop(&sessions, lks->id); - free(lks); -} - -static void -lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) -{ - struct forward_req fwreq; - struct envelope ep; - struct expandnode node; - struct mailaddr maddr; - struct dispatcher *dsp; - struct table *userbase; - int r; - union lookup lk; - char *tag; - const char *srs_decoded; - - if (xn->depth >= EXPAND_DEPTH) { - log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep."); - lks->error = LKA_PERMFAIL; - lks->errormsg = "524 5.2.4 Mailing list expansion problem"; - return; - } - - switch (xn->type) { - case EXPAND_INVALID: - case EXPAND_INCLUDE: - fatalx("lka_expand: unexpected type"); - break; - - case EXPAND_ADDRESS: - - log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s " - "[depth=%d]", - xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth); - - - ep = lks->envelope; - ep.dest = xn->u.mailaddr; - if (xn->parent) /* nodes with parent are forward addresses */ - ep.flags |= EF_INTERNAL; - - /* handle SRS */ - if (env->sc_srs_key != NULL && - ep.sender.user[0] == '\0' && - (strncasecmp(ep.rcpt.user, "SRS0=", 5) == 0 || - strncasecmp(ep.rcpt.user, "SRS1=", 5) == 0)) { - srs_decoded = srs_decode(mailaddr_to_text(&ep.rcpt)); - if (srs_decoded && - text_to_mailaddr(&ep.rcpt, srs_decoded)) { - /* flag envelope internal and override rcpt */ - ep.flags |= EF_INTERNAL; - xn->u.mailaddr = ep.rcpt; - lks->envelope = ep; - } - else { - log_warn("SRS failed to decode: %s", - mailaddr_to_text(&ep.rcpt)); - } - } - - /* Pass the node through the ruleset */ - rule = ruleset_match(&ep); - if (rule == NULL || rule->reject) { - lks->error = (errno == EAGAIN) ? - LKA_TEMPFAIL : LKA_PERMFAIL; - break; - } - - dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); - if (dsp->type == DISPATCHER_REMOTE) { - lka_submit(lks, rule, xn); - } - else if (dsp->u.local.table_virtual) { - /* expand */ - lks->expand.rule = rule; - lks->expand.parent = xn; - - /* temporary replace the mailaddr with a copy where - * we eventually strip the '+'-part before lookup. - */ - maddr = xn->u.mailaddr; - xlowercase(maddr.user, xn->u.mailaddr.user, - sizeof maddr.user); - r = aliases_virtual_get(&lks->expand, &maddr); - if (r == -1) { - lks->error = LKA_TEMPFAIL; - log_trace(TRACE_EXPAND, "expand: lka_expand: " - "error in virtual alias lookup"); - } - else if (r == 0) { - lks->error = LKA_PERMFAIL; - log_trace(TRACE_EXPAND, "expand: lka_expand: " - "no aliases for virtual"); - } - if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL) - lks->errormsg = "424 4.2.4 Mailing list expansion problem"; - if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL) - lks->errormsg = "524 5.2.4 Mailing list expansion problem"; - } - else { - lks->expand.rule = rule; - lks->expand.parent = xn; - xn->rule = rule; - - memset(&node, 0, sizeof node); - node.type = EXPAND_USERNAME; - xlowercase(node.u.user, xn->u.mailaddr.user, - sizeof node.u.user); - expand_insert(&lks->expand, &node); - } - break; - - case EXPAND_USERNAME: - log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s " - "[depth=%d, sameuser=%d]", - xn->u.user, xn->depth, xn->sameuser); - - /* expand aliases with the given rule */ - dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); - - lks->expand.rule = rule; - lks->expand.parent = xn; - - if (!xn->sameuser && - (dsp->u.local.table_alias || dsp->u.local.table_virtual)) { - if (dsp->u.local.table_alias) - r = aliases_get(&lks->expand, xn->u.user); - if (dsp->u.local.table_virtual) - r = aliases_virtual_get(&lks->expand, &xn->u.mailaddr); - if (r == -1) { - log_trace(TRACE_EXPAND, "expand: lka_expand: " - "error in alias lookup"); - lks->error = LKA_TEMPFAIL; - if (lks->errormsg == NULL) - lks->errormsg = "424 4.2.4 Mailing list expansion problem"; - } - if (r) - break; - } - - /* gilles+hackers@ -> gilles@ */ - if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL) { - *tag++ = '\0'; - (void)strlcpy(xn->subaddress, tag, sizeof xn->subaddress); - } - - userbase = table_find(env, dsp->u.local.table_userbase); - r = table_lookup(userbase, K_USERINFO, xn->u.user, &lk); - if (r == -1) { - log_trace(TRACE_EXPAND, "expand: lka_expand: " - "backend error while searching user"); - lks->error = LKA_TEMPFAIL; - break; - } - if (r == 0) { - log_trace(TRACE_EXPAND, "expand: lka_expand: " - "user-part does not match system user"); - lks->error = LKA_PERMFAIL; - break; - } - xn->realuser = 1; - - if (xn->sameuser && xn->parent->forwarded) { - log_trace(TRACE_EXPAND, "expand: lka_expand: same " - "user, submitting"); - lka_submit(lks, rule, xn); - break; - } - - /* no aliases found, query forward file */ - lks->rule = rule; - lks->node = xn; - xn->forwarded = 1; - - memset(&fwreq, 0, sizeof(fwreq)); - fwreq.id = lks->id; - (void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user)); - (void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory)); - fwreq.uid = lk.userinfo.uid; - fwreq.gid = lk.userinfo.gid; - - m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1, - &fwreq, sizeof(fwreq)); - lks->flags |= F_WAITING; - break; - - case EXPAND_FILENAME: - dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); - if (dsp->u.local.forward_only) { - log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule"); - lks->error = LKA_TEMPFAIL; - break; - } - log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s " - "[depth=%d]", xn->u.buffer, xn->depth); - lka_submit(lks, rule, xn); - break; - - case EXPAND_ERROR: - dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); - if (dsp->u.local.forward_only) { - log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule"); - lks->error = LKA_TEMPFAIL; - break; - } - log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s " - "[depth=%d]", xn->u.buffer, xn->depth); - if (xn->u.buffer[0] == '4') - lks->error = LKA_TEMPFAIL; - else if (xn->u.buffer[0] == '5') - lks->error = LKA_PERMFAIL; - lks->errormsg = xn->u.buffer; - break; - - case EXPAND_FILTER: - dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); - if (dsp->u.local.forward_only) { - log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule"); - lks->error = LKA_TEMPFAIL; - break; - } - log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s " - "[depth=%d]", xn->u.buffer, xn->depth); - lka_submit(lks, rule, xn); - break; - } -} - -static struct expandnode * -lka_find_ancestor(struct expandnode *xn, enum expand_type type) -{ - while (xn && (xn->type != type)) - xn = xn->parent; - if (xn == NULL) { - log_warnx("warn: lka_find_ancestor: no ancestors of type %d", - type); - fatalx(NULL); - } - return (xn); -} - -static void -lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) -{ - struct envelope *ep; - struct dispatcher *dsp; - const char *user; - const char *format; - - ep = xmemdup(&lks->envelope, sizeof *ep); - (void)strlcpy(ep->dispatcher, rule->dispatcher, sizeof ep->dispatcher); - - dsp = dict_xget(env->sc_dispatchers, ep->dispatcher); - - switch (dsp->type) { - case DISPATCHER_REMOTE: - if (xn->type != EXPAND_ADDRESS) - fatalx("lka_deliver: expect address"); - ep->type = D_MTA; - ep->dest = xn->u.mailaddr; - break; - - case DISPATCHER_BOUNCE: - case DISPATCHER_LOCAL: - if (xn->type != EXPAND_USERNAME && - xn->type != EXPAND_FILENAME && - xn->type != EXPAND_FILTER) - fatalx("lka_deliver: wrong type: %d", xn->type); - - ep->type = D_MDA; - ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr; - if (xn->type == EXPAND_USERNAME) { - (void)strlcpy(ep->mda_user, xn->u.user, sizeof(ep->mda_user)); - (void)strlcpy(ep->mda_subaddress, xn->subaddress, sizeof(ep->mda_subaddress)); - } - else { - user = !xn->parent->realuser ? - SMTPD_USER : - xn->parent->u.user; - (void)strlcpy(ep->mda_user, user, sizeof (ep->mda_user)); - - /* this battle needs to be fought ... */ - if (xn->type == EXPAND_FILTER && - strcmp(ep->mda_user, SMTPD_USER) == 0) - log_warnx("commands executed from aliases " - "run with %s privileges", SMTPD_USER); - - if (xn->type == EXPAND_FILENAME) - format = PATH_LIBEXEC"/mail.mboxfile -f %%{mbox.from} %s"; - else if (xn->type == EXPAND_FILTER) - format = "%s"; - (void)snprintf(ep->mda_exec, sizeof(ep->mda_exec), - format, xn->u.buffer); - } - break; - } - - TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry); -} diff --git a/smtpd/log.c b/smtpd/log.c deleted file mode 100644 index 14f681e3..00000000 --- a/smtpd/log.c +++ /dev/null @@ -1,220 +0,0 @@ -/* $OpenBSD: log.c,v 1.20 2017/03/21 12:06:56 bluhm Exp $ */ - -/* - * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <syslog.h> -#include <errno.h> -#include <time.h> - -static int debug; -static int verbose; -const char *log_procname; - -void log_init(int, int); -void log_procinit(const char *); -void log_setverbose(int); -int log_getverbose(void); -void log_warn(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_warnx(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_info(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_debug(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void logit(int, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void vlog(int, const char *, va_list) - __attribute__((__format__ (printf, 2, 0))); -__dead void fatal(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -__dead void fatalx(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); - -void -log_init(int n_debug, int facility) -{ - extern char *__progname; - - debug = n_debug; - verbose = n_debug; - log_procinit(__progname); - - if (!debug) - openlog(__progname, LOG_PID | LOG_NDELAY, facility); - - tzset(); -} - -void -log_procinit(const char *procname) -{ - if (procname != NULL) - log_procname = procname; -} - -void -log_setverbose(int v) -{ - verbose = v; -} - -int -log_getverbose(void) -{ - return (verbose); -} - -void -logit(int pri, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vlog(pri, fmt, ap); - va_end(ap); -} - -void -vlog(int pri, const char *fmt, va_list ap) -{ - char *nfmt; - int saved_errno = errno; - - if (debug) { - /* best effort in out of mem situations */ - if (asprintf(&nfmt, "%s\n", fmt) == -1) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } else { - vfprintf(stderr, nfmt, ap); - free(nfmt); - } - fflush(stderr); - } else - vsyslog(pri, fmt, ap); - - errno = saved_errno; -} - -void -log_warn(const char *emsg, ...) -{ - char *nfmt; - va_list ap; - int saved_errno = errno; - - /* best effort to even work in out of memory situations */ - if (emsg == NULL) - logit(LOG_ERR, "%s", strerror(saved_errno)); - else { - va_start(ap, emsg); - - if (asprintf(&nfmt, "%s: %s", emsg, - strerror(saved_errno)) == -1) { - /* we tried it... */ - vlog(LOG_ERR, emsg, ap); - logit(LOG_ERR, "%s", strerror(saved_errno)); - } else { - vlog(LOG_ERR, nfmt, ap); - free(nfmt); - } - va_end(ap); - } - - errno = saved_errno; -} - -void -log_warnx(const char *emsg, ...) -{ - va_list ap; - - va_start(ap, emsg); - vlog(LOG_ERR, emsg, ap); - va_end(ap); -} - -void -log_info(const char *emsg, ...) -{ - va_list ap; - - va_start(ap, emsg); - vlog(LOG_INFO, emsg, ap); - va_end(ap); -} - -void -log_debug(const char *emsg, ...) -{ - va_list ap; - - if (verbose > 1) { - va_start(ap, emsg); - vlog(LOG_DEBUG, emsg, ap); - va_end(ap); - } -} - -static void -vfatalc(int code, const char *emsg, va_list ap) -{ - static char s[BUFSIZ]; - const char *sep; - - if (emsg != NULL) { - (void)vsnprintf(s, sizeof(s), emsg, ap); - sep = ": "; - } else { - s[0] = '\0'; - sep = ""; - } - if (code) - logit(LOG_CRIT, "%s: %s%s%s", - log_procname, s, sep, strerror(code)); - else - logit(LOG_CRIT, "%s%s%s", log_procname, sep, s); -} - -void -fatal(const char *emsg, ...) -{ - va_list ap; - - va_start(ap, emsg); - vfatalc(errno, emsg, ap); - va_end(ap); - exit(1); -} - -void -fatalx(const char *emsg, ...) -{ - va_list ap; - - va_start(ap, emsg); - vfatalc(0, emsg, ap); - va_end(ap); - exit(1); -} diff --git a/smtpd/log.h b/smtpd/log.h deleted file mode 100644 index 81d0973c..00000000 --- a/smtpd/log.h +++ /dev/null @@ -1,52 +0,0 @@ -/* $OpenBSD: log.h,v 1.8 2018/04/26 20:57:59 eric Exp $ */ - -/* - * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LOG_H -#define LOG_H - -#include "openbsd-compat.h" - -#include <syslog.h> - -#include <stdarg.h> -#ifdef HAVE_SYS_CDEFS_H -#include <sys/cdefs.h> -#endif - -void log_init(int, int); -void log_procinit(const char *); -void log_setverbose(int); -int log_getverbose(void); -void log_warn(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_warnx(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_info(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_debug(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void logit(int, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void vlog(int, const char *, va_list) - __attribute__((__format__ (printf, 2, 0))); -__dead void fatal(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -__dead void fatalx(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); - -#endif /* LOG_H */ diff --git a/smtpd/mail.lmtp.8 b/smtpd/mail.lmtp.8 deleted file mode 100644 index 98dee00d..00000000 --- a/smtpd/mail.lmtp.8 +++ /dev/null @@ -1,55 +0,0 @@ -.\" $OpenBSD: mail.lmtp.8,v 1.1 2017/02/14 15:16:34 gilles Exp $ -.\" -.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: February 14 2017 $ -.Dt MAIL.LMTP 8 -.Os -.Sh NAME -.Nm mail.lmtp -.Nd deliver mail through LMTP -.Sh SYNOPSIS -.Nm mail.lmtp -.Op Fl d Ar destination -.Op Fl f Ar from -.Op Fl l Ar lhlo -.Ar user ... -.Sh DESCRIPTION -.Nm -reads the standard input up to an end-of-file and delivers it to -an LMTP server for each -.Ar user Ns 's -address. -The -.Ar user -must be a valid user name or email address. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl d Ar destination -Specify the destination LMTP address. -.It Fl f Ar from -Specify the sender's name or email address. -.It Fl l Ar lhlo -Specify the LHLO argument used in the LMTP session. -By default, -.Nm mail.lmtp -will default to "localhost". -.El -.Sh EXIT STATUS -.Ex -std mail.lmtp -.Sh SEE ALSO -.Xr mail 1 , -.Xr smtpd 8 diff --git a/smtpd/mail.lmtp.c b/smtpd/mail.lmtp.c deleted file mode 100644 index 90b89990..00000000 --- a/smtpd/mail.lmtp.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <unistd.h> - -enum phase { - PHASE_BANNER, - PHASE_HELO, - PHASE_MAILFROM, - PHASE_RCPTTO, - PHASE_DATA, - PHASE_EOM, - PHASE_QUIT -}; - -struct session { - const char *lhlo; - const char *mailfrom; - char *rcptto; - - char **rcpts; - int n_rcpts; -}; - -static int lmtp_connect(const char *); -static void lmtp_engine(int, struct session *); -static void stream_file(FILE *); - -int -main(int argc, char *argv[]) -{ - int ch; - int conn; - const char *destination = "localhost"; - struct session session; - - if (! geteuid()) - errx(EX_TEMPFAIL, "mail.lmtp: may not be executed as root"); - - session.lhlo = "localhost"; - session.mailfrom = getenv("SENDER"); - session.rcptto = NULL; - - while ((ch = getopt(argc, argv, "d:l:f:ru")) != -1) { - switch (ch) { - case 'd': - destination = optarg; - break; - case 'l': - session.lhlo = optarg; - break; - case 'f': - session.mailfrom = optarg; - break; - - case 'r': - session.rcptto = getenv("RECIPIENT"); - break; - - case 'u': - session.rcptto = getenv("USER"); - break; - - default: - break; - } - } - argc -= optind; - argv += optind; - - if (session.mailfrom == NULL) - errx(EX_TEMPFAIL, "sender must be specified with -f"); - - if (argc == 0 && session.rcptto == NULL) - errx(EX_TEMPFAIL, "no recipient was specified"); - - if (session.rcptto) { - session.rcpts = &session.rcptto; - session.n_rcpts = 1; - } - else { - session.rcpts = argv; - session.n_rcpts = argc; - } - - conn = lmtp_connect(destination); - lmtp_engine(conn, &session); - - return (0); -} - -static int -lmtp_connect_inet(const char *destination) -{ - struct addrinfo hints, *res, *res0; - char *destcopy = NULL; - const char *hostname = NULL; - const char *servname = NULL; - const char *cause = NULL; - char *p; - int n, s = -1, save_errno; - - if ((destcopy = strdup(destination)) == NULL) - err(EX_TEMPFAIL, NULL); - - servname = "25"; - hostname = destcopy; - p = destcopy; - if (*p == '[') { - if ((p = strchr(destcopy, ']')) == NULL) - errx(EX_TEMPFAIL, "inet: invalid address syntax"); - - /* remove [ and ] */ - *p = '\0'; - hostname++; - if (strncasecmp(hostname, "IPv6:", 5) == 0) - hostname += 5; - - /* extract port if any */ - switch (*(p+1)) { - case ':': - servname = p+2; - break; - case '\0': - break; - default: - errx(EX_TEMPFAIL, "inet: invalid address syntax"); - } - } - else if ((p = strchr(destcopy, ':')) != NULL) { - *p++ = '\0'; - servname = p; - } - - memset(&hints, 0, sizeof hints); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - n = getaddrinfo(hostname, servname, &hints, &res0); - if (n) - errx(EX_TEMPFAIL, "inet: %s", gai_strerror(n)); - - for (res = res0; res; res = res->ai_next) { - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s == -1) { - cause = "socket"; - continue; - } - - if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { - cause = "connect"; - save_errno = errno; - close(s); - errno = save_errno; - s = -1; - continue; - } - break; - } - - freeaddrinfo(res0); - if (s == -1) - errx(EX_TEMPFAIL, "%s", cause); - - free(destcopy); - return s; -} - -static int -lmtp_connect_unix(const char *destination) -{ - struct sockaddr_un addr; - int s; - - if (*destination != '/') - errx(EX_TEMPFAIL, "unix: path must be absolute"); - - if ((s = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) - err(EX_TEMPFAIL, NULL); - - memset(&addr, 0, sizeof addr); - addr.sun_family = AF_UNIX; - if (strlcpy(addr.sun_path, destination, sizeof addr.sun_path) - >= sizeof addr.sun_path) - errx(EX_TEMPFAIL, "unix: socket path is too long"); - - if (connect(s, (struct sockaddr *)&addr, sizeof addr) == -1) - err(EX_TEMPFAIL, "connect"); - - return s; -} - -static int -lmtp_connect(const char *destination) -{ - if (destination[0] == '/') - return lmtp_connect_unix(destination); - return lmtp_connect_inet(destination); -} - -static void -lmtp_engine(int fd_read, struct session *session) -{ - int fd_write = 0; - FILE *file_read = 0; - FILE *file_write = 0; - char *line = NULL; - size_t linesize = 0; - ssize_t linelen; - enum phase phase = PHASE_BANNER; - - if ((fd_write = dup(fd_read)) == -1) - err(EX_TEMPFAIL, "dup"); - - if ((file_read = fdopen(fd_read, "r")) == NULL) - err(EX_TEMPFAIL, "fdopen"); - - if ((file_write = fdopen(fd_write, "w")) == NULL) - err(EX_TEMPFAIL, "fdopen"); - - do { - fflush(file_write); - - if ((linelen = getline(&line, &linesize, file_read)) == -1) { - if (ferror(file_read)) - err(EX_TEMPFAIL, "getline"); - else - errx(EX_TEMPFAIL, "unexpected EOF from LMTP server"); - } - line[strcspn(line, "\n")] = '\0'; - line[strcspn(line, "\r")] = '\0'; - - if (linelen < 4 || - !isdigit((unsigned char)line[0]) || - !isdigit((unsigned char)line[1]) || - !isdigit((unsigned char)line[2]) || - (line[3] != ' ' && line[3] != '-')) - errx(EX_TEMPFAIL, "LMTP server sent an invalid line"); - - if (line[0] != (phase == PHASE_DATA ? '3' : '2')) - errx(EX_TEMPFAIL, "LMTP server error: %s", line); - - if (line[3] == '-') - continue; - - switch (phase) { - - case PHASE_BANNER: - fprintf(file_write, "LHLO %s\r\n", session->lhlo); - phase++; - break; - - case PHASE_HELO: - fprintf(file_write, "MAIL FROM:<%s>\r\n", session->mailfrom); - phase++; - break; - - case PHASE_MAILFROM: - fprintf(file_write, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]); - if (session->n_rcpts - 1 == 0) { - phase++; - break; - } - session->n_rcpts--; - break; - - case PHASE_RCPTTO: - fprintf(file_write, "DATA\r\n"); - phase++; - break; - - case PHASE_DATA: - stream_file(file_write); - fprintf(file_write, ".\r\n"); - phase++; - break; - - case PHASE_EOM: - fprintf(file_write, "QUIT\r\n"); - phase++; - break; - - case PHASE_QUIT: - exit(0); - } - } while (1); -} - -static void -stream_file(FILE *conn) -{ - char *line = NULL; - size_t linesize = 0; - ssize_t linelen; - - while ((linelen = getline(&line, &linesize, stdin)) != -1) { - line[strcspn(line, "\n")] = '\0'; - if (line[0] == '.') - fprintf(conn, "."); - fprintf(conn, "%s\r\n", line); - } - free(line); - if (ferror(stdin)) - err(EX_TEMPFAIL, "getline"); -} diff --git a/smtpd/mail.maildir.8 b/smtpd/mail.maildir.8 deleted file mode 100644 index ce822698..00000000 --- a/smtpd/mail.maildir.8 +++ /dev/null @@ -1,45 +0,0 @@ -.\" $OpenBSD: mail.maildir.8,v 1.5 2018/05/30 12:37:57 jmc Exp $ -.\" -.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: May 30 2018 $ -.Dt MAIL.MAILDIR 8 -.Os -.Sh NAME -.Nm mail.maildir -.Nd store mail in a maildir -.Sh SYNOPSIS -.Nm mail.maildir -.Op Fl j -.Op Ar pathname -.Sh DESCRIPTION -.Nm -reads the standard input up to an end-of-file and adds it to the -mail directory located in -.Ar pathname -or to the mail directory -.Pa Maildir -located in the user's home directory. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl j -Scan message for X-Spam and move to Junk folder if result is positive. -.El -.Sh EXIT STATUS -.Ex -std mail.maildir -.Sh SEE ALSO -.Xr mail 1 , -.Xr smtpd 8 diff --git a/smtpd/mail.maildir.c b/smtpd/mail.maildir.c deleted file mode 100644 index fe6adba6..00000000 --- a/smtpd/mail.maildir.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) -#endif - -#include <sys/types.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <sysexits.h> -#include <unistd.h> - -#define MAILADDR_ESCAPE "!#$%&'*/?^`{|}~" - -static int maildir_subdir(const char *, char *, size_t); -static void maildir_mkdirs(const char *); -static void maildir_engine(const char *, int); -static int mkdirs_component(const char *, mode_t); -static int mkdirs(const char *, mode_t); - -int -main(int argc, char *argv[]) -{ - int ch; - int junk = 0; - - if (! geteuid()) - errx(1, "mail.maildir: may not be executed as root"); - - while ((ch = getopt(argc, argv, "j")) != -1) { - switch (ch) { - case 'j': - junk = 1; - break; - default: - break; - } - } - argc -= optind; - argv += optind; - - if (argc > 1) - errx(1, "mail.maildir: only one maildir is allowed"); - - maildir_engine(argv[0], junk); - - return (0); -} - -static int -maildir_subdir(const char *extension, char *dest, size_t len) -{ - char *sanitized; - - if (strlcpy(dest, extension, len) >= len) - return 0; - - for (sanitized = dest; *sanitized; sanitized++) - if (strchr(MAILADDR_ESCAPE, *sanitized)) - *sanitized = ':'; - - return 1; -} - -static void -maildir_mkdirs(const char *dirname) -{ - uint i; - int ret; - char pathname[PATH_MAX]; - char *subdirs[] = { "cur", "tmp", "new" }; - - if (mkdirs(dirname, 0700) == -1 && errno != EEXIST) { - if (errno == EINVAL || errno == ENAMETOOLONG) - err(1, NULL); - err(EX_TEMPFAIL, NULL); - } - - for (i = 0; i < nitems(subdirs); ++i) { - ret = snprintf(pathname, sizeof pathname, "%s/%s", dirname, - subdirs[i]); - if (ret < 0 || (size_t)ret >= sizeof pathname) - errc(1, ENAMETOOLONG, "%s/%s", dirname, subdirs[i]); - if (mkdir(pathname, 0700) == -1 && errno != EEXIST) - err(EX_TEMPFAIL, NULL); - } -} - -static void -maildir_engine(const char *dirname, int junk) -{ - char rootpath[PATH_MAX]; - char junkpath[PATH_MAX]; - char extpath[PATH_MAX]; - char subdir[PATH_MAX]; - char filename[PATH_MAX]; - char hostname[HOST_NAME_MAX+1]; - - char tmp[PATH_MAX]; - char new[PATH_MAX]; - - int ret; - - int fd; - FILE *fp; - char *line = NULL; - size_t linesize = 0; - ssize_t linelen; - struct stat sb; - char *home; - char *extension; - - int is_junk = 0; - int in_hdr = 1; - - if (dirname == NULL) { - if ((home = getenv("HOME")) == NULL) - err(1, NULL); - ret = snprintf(rootpath, sizeof rootpath, "%s/Maildir", home); - if (ret < 0 || (size_t)ret >= sizeof rootpath) - errc(1, ENAMETOOLONG, "%s/Maildir", home); - dirname = rootpath; - } - maildir_mkdirs(dirname); - - if (junk) { - /* create Junk subdirectory */ - ret = snprintf(junkpath, sizeof junkpath, "%s/.Junk", dirname); - if (ret < 0 || (size_t)ret >= sizeof junkpath) - errc(1, ENAMETOOLONG, "%s/.Junk", dirname); - maildir_mkdirs(junkpath); - } - - if ((extension = getenv("EXTENSION")) != NULL) { - if (maildir_subdir(extension, subdir, sizeof(subdir)) && - subdir[0]) { - ret = snprintf(extpath, sizeof extpath, "%s/.%s", - dirname, subdir); - if (ret < 0 || (size_t)ret >= sizeof extpath) - errc(1, ENAMETOOLONG, "%s/.%s", - dirname, subdir); - if (stat(extpath, &sb) != -1) { - dirname = extpath; - maildir_mkdirs(dirname); - } - } - } - - if (gethostname(hostname, sizeof hostname) != 0) - (void)strlcpy(hostname, "localhost", sizeof hostname); - - (void)snprintf(filename, sizeof filename, "%lld.%08x.%s", - (long long int) time(NULL), - arc4random(), - hostname); - - (void)snprintf(tmp, sizeof tmp, "%s/tmp/%s", dirname, filename); - - fd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0600); - if (fd == -1) - err(EX_TEMPFAIL, NULL); - if ((fp = fdopen(fd, "w")) == NULL) - err(EX_TEMPFAIL, NULL); - - while ((linelen = getline(&line, &linesize, stdin)) != -1) { - line[strcspn(line, "\n")] = '\0'; - if (line[0] == '\0') - in_hdr = 0; - if (junk && in_hdr && - (strcasecmp(line, "x-spam: yes") == 0 || - strcasecmp(line, "x-spam-flag: yes") == 0)) - is_junk = 1; - fprintf(fp, "%s\n", line); - } - free(line); - if (ferror(stdin)) - err(EX_TEMPFAIL, NULL); - - if (fflush(fp) == EOF || - ferror(fp) || - fsync(fd) == -1 || - fclose(fp) == EOF) - err(EX_TEMPFAIL, NULL); - - (void)snprintf(new, sizeof new, "%s/new/%s", - is_junk ? junkpath : dirname, filename); - - if (rename(tmp, new) == -1) - err(EX_TEMPFAIL, NULL); - - exit(0); -} - - -static int -mkdirs_component(const char *path, mode_t mode) -{ - struct stat sb; - - if (stat(path, &sb) == -1) { - if (errno != ENOENT) - return 0; - if (mkdir(path, mode | S_IWUSR | S_IXUSR) == -1) - return 0; - } - else if (!S_ISDIR(sb.st_mode)) { - errno = ENOTDIR; - return 0; - } - - return 1; -} - -static int -mkdirs(const char *path, mode_t mode) -{ - char buf[PATH_MAX]; - int i = 0; - int done = 0; - const char *p; - - /* absolute path required */ - if (*path != '/') { - errno = EINVAL; - return 0; - } - - /* make sure we don't exceed PATH_MAX */ - if (strlen(path) >= sizeof buf) { - errno = ENAMETOOLONG; - return 0; - } - - memset(buf, 0, sizeof buf); - for (p = path; *p; p++) { - if (*p == '/') { - if (buf[0] != '\0') - if (!mkdirs_component(buf, mode)) - return 0; - while (*p == '/') - p++; - buf[i++] = '/'; - buf[i++] = *p; - if (*p == '\0' && ++done) - break; - continue; - } - buf[i++] = *p; - } - if (!done) - if (!mkdirs_component(buf, mode)) - return 0; - - if (chmod(path, mode) == -1) - return 0; - - return 1; -} diff --git a/smtpd/mail.mboxfile.8 b/smtpd/mail.mboxfile.8 deleted file mode 100644 index 015adcb5..00000000 --- a/smtpd/mail.mboxfile.8 +++ /dev/null @@ -1,34 +0,0 @@ -.\" $OpenBSD: mail.mboxfile.8,v 1.1 2018/07/25 10:19:28 gilles Exp $ -.\" -.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: July 25 2018 $ -.Dt MAIL.MDA 8 -.Os -.Sh NAME -.Nm mail.mboxfile -.Nd deliver mail to a file in mbox format -.Sh SYNOPSIS -.Nm mail.mboxfile -.Ar file -.Sh DESCRIPTION -.Nm -appends mail to a file in mbox format and acknowledges delivery success or failure -with its exit status. -.Sh EXIT STATUS -.Ex -std mail.mboxfile -.Sh SEE ALSO -.Xr mail 1 , -.Xr smtpd 8 diff --git a/smtpd/mail.mboxfile.c b/smtpd/mail.mboxfile.c deleted file mode 100644 index 097a8d96..00000000 --- a/smtpd/mail.mboxfile.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <time.h> -#include <unistd.h> - -static void mboxfile_engine(const char *sender, const char *filename); - -int -main(int argc, char *argv[]) -{ - int ch; - char *sender = "<unknown>"; - - if (! geteuid()) - errx(1, "mail.mboxfile: may not be executed as root"); - - while ((ch = getopt(argc, argv, "f:")) != -1) { - switch (ch) { - case 'f': - sender = optarg; - break; - default: - break; - } - } - argc -= optind; - argv += optind; - - if (argc > 1) - errx(1, "mail.mboxfile: only one mboxfile is allowed"); - - mboxfile_engine(sender, argv[0]); - - return (0); -} - -static void -mboxfile_engine(const char *sender, const char *filename) -{ - int fd; - FILE *fp; - char *line = NULL; - size_t linesize = 0; - ssize_t linelen; - time_t now; - - time(&now); - -#ifndef O_EXLOCK -#define O_EXLOCK 0 -#endif - fd = open(filename, O_CREAT | O_APPEND | O_WRONLY | O_EXLOCK, 0600); -#ifndef HAVE_O_EXLOCK - /* XXX : do something! */ -#endif - if (fd == -1) - err(EX_TEMPFAIL, NULL); - - if ((fp = fdopen(fd, "w")) == NULL) - err(EX_TEMPFAIL, NULL); - - fprintf(fp, "From %s %s", sender, ctime(&now)); - while ((linelen = getline(&line, &linesize, stdin)) != -1) { - line[strcspn(line, "\n")] = '\0'; - if (strncmp(line, "From ", 5) == 0) - fprintf(fp, ">%s\n", line); - else - fprintf(fp, "%s\n", line); - } - fprintf(fp, "\n"); - free(line); - if (ferror(stdin)) - err(EX_TEMPFAIL, NULL); - - if (fflush(fp) == EOF || - ferror(fp) || - (fsync(fd) == -1 && errno != EINVAL) || - fclose(fp) == EOF) - err(EX_TEMPFAIL, NULL); -} diff --git a/smtpd/mail.mda.8 b/smtpd/mail.mda.8 deleted file mode 100644 index 61fed733..00000000 --- a/smtpd/mail.mda.8 +++ /dev/null @@ -1,35 +0,0 @@ -.\" $OpenBSD: mail.mda.8,v 1.1 2017/08/09 07:56:10 gilles Exp $ -.\" -.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: August 9 2017 $ -.Dt MAIL.MDA 8 -.Os -.Sh NAME -.Nm mail.mda -.Nd deliver mail to a program -.Sh SYNOPSIS -.Nm mail.mda -.Ar program -.Sh DESCRIPTION -.Nm -executes the program and its parameters. -The program must read from the standard input up to an end-of-file -and acknowledge delivery success or failure with its exit status. -.Sh EXIT STATUS -.Ex -std mail.mda -.Sh SEE ALSO -.Xr mail 1 , -.Xr smtpd 8 diff --git a/smtpd/mail.mda.c b/smtpd/mail.mda.c deleted file mode 100644 index 23958071..00000000 --- a/smtpd/mail.mda.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <unistd.h> - -int -main(int argc, char *argv[]) -{ - int ch; - int ret; - - if (! geteuid()) - errx(1, "mail.mda: may not be executed as root"); - - while ((ch = getopt(argc, argv, "")) != -1) { - switch (ch) { - default: - break; - } - } - argc -= optind; - argv += optind; - - if (argc == 0) - errx(1, "mail.mda: command required"); - - if (argc > 1) - errx(1, "mail.mda: only one command is supported"); - - /* could not obtain a shell or could not obtain wait status, - * tempfail */ - if ((ret = system(argv[0])) == -1) - errx(EX_TEMPFAIL, "%s", strerror(errno)); - - /* not exited properly but we have no details, - * tempfail */ - if (! WIFEXITED(ret)) - exit(EX_TEMPFAIL); - - exit(WEXITSTATUS(ret)); -} diff --git a/smtpd/mail/Makefile b/smtpd/mail/Makefile deleted file mode 100644 index b2bc2a26..00000000 --- a/smtpd/mail/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# $OpenBSD: Makefile,v 1.8 2018/07/25 10:19:28 gilles Exp $ -.PATH: ${.CURDIR}/.. - -PROGS= mail.lmtp mail.maildir mail.mboxfile mail.mda - -MAN= mail.lmtp.8 mail.maildir.8 mail.mboxfile.8 mail.mda.8 - -BINOWN= root -BINGRP= wheel - -BINDIR= /usr/libexec - -CFLAGS+= -fstack-protector-all -CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes -CFLAGS+= -Wmissing-declarations -CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual -CFLAGS+= -Wsign-compare -CFLAGS+= -Werror-implicit-function-declaration - -.include <bsd.prog.mk> diff --git a/smtpd/mailaddr.c b/smtpd/mailaddr.c deleted file mode 100644 index 4346e3dc..00000000 --- a/smtpd/mailaddr.c +++ /dev/null @@ -1,135 +0,0 @@ -/* $OpenBSD: mailaddr.c,v 1.3 2018/05/31 21:06:12 gilles Exp $ */ - -/* - * Copyright (c) 2015 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - -static int -mailaddr_line_split(char **line, char **ret) -{ - static char buffer[LINE_MAX]; - int esc, dq, sq; - size_t i; - char *s; - - memset(buffer, 0, sizeof buffer); - esc = dq = sq = 0; - i = 0; - for (s = *line; (*s) && (i < sizeof(buffer)); ++s) { - if (esc) { - buffer[i++] = *s; - esc = 0; - continue; - } - if (*s == '\\') { - esc = 1; - continue; - } - if (*s == ',' && !dq && !sq) { - *ret = buffer; - *line = s+1; - return (1); - } - - buffer[i++] = *s; - esc = 0; - - if (*s == '"' && !sq) - dq ^= 1; - if (*s == '\'' && !dq) - sq ^= 1; - } - - if (esc || dq || sq || i == sizeof(buffer)) - return (-1); - - *ret = buffer; - *line = s; - return (i ? 1 : 0); -} - -int -mailaddr_line(struct maddrmap *maddrmap, const char *s) -{ - struct maddrnode mn; - char buffer[LINE_MAX]; - char *p, *subrcpt; - int ret; - - memset(buffer, 0, sizeof buffer); - if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) - return 0; - - p = buffer; - while ((ret = mailaddr_line_split(&p, &subrcpt)) > 0) { - subrcpt = strip(subrcpt); - if (subrcpt[0] == '\0') - continue; - if (!text_to_mailaddr(&mn.mailaddr, subrcpt)) - return 0; - log_debug("subrcpt: [%s]", subrcpt); - maddrmap_insert(maddrmap, &mn); - } - - if (ret >= 0) - return 1; - /* expand_line_split() returned < 0 */ - return 0; -} - -void -maddrmap_init(struct maddrmap *maddrmap) -{ - TAILQ_INIT(&maddrmap->queue); -} - -void -maddrmap_insert(struct maddrmap *maddrmap, struct maddrnode *maddrnode) -{ - struct maddrnode *mn; - - mn = xmemdup(maddrnode, sizeof *maddrnode); - TAILQ_INSERT_TAIL(&maddrmap->queue, mn, entries); -} - -void -maddrmap_free(struct maddrmap *maddrmap) -{ - struct maddrnode *mn; - - while ((mn = TAILQ_FIRST(&maddrmap->queue))) { - TAILQ_REMOVE(&maddrmap->queue, mn, entries); - free(mn); - } - free(maddrmap); -} diff --git a/smtpd/makemap.8 b/smtpd/makemap.8 deleted file mode 100644 index 674bef6f..00000000 --- a/smtpd/makemap.8 +++ /dev/null @@ -1,174 +0,0 @@ -.\" $OpenBSD: makemap.8,v 1.30 2018/11/25 14:41:16 gilles Exp $ -.\" -.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@openbsd.org> -.\" Copyright (c) 2008-2009 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: November 25 2018 $ -.Dt MAKEMAP 8 -.Os -.Sh NAME -.Nm makemap -.Nd create database maps for smtpd -.Sh SYNOPSIS -.Nm makemap -.Op Fl U -.Op Fl d Ar dbtype -.Op Fl o Ar dbfile -.Op Fl t Ar type -.Ar file -.Sh DESCRIPTION -Maps provide a generic interface for associating textual key to a value. -Such associations may be accessed through a plaintext file, database, or DNS. -The format of these file types is described below. -.Nm -itself creates the database maps used by keyed map lookups specified in -.Xr smtpd.conf 5 . -.Pp -.Nm -reads input from -.Ar file -and writes data to a file whose name is made by adding a -.Dq .db -suffix to -.Ar file . -The current line can be extended over multiple lines using a backslash -.Pq Sq \e . -Comments can be put anywhere in the file using a hash mark -.Pq Sq # , -and extend to the end of the current line. -Care should be taken when commenting out multi-line text: -the comment is effective until the end of the entire block. -In all cases, -.Nm -reads lines consisting of words separated by whitespace. -The first word of a line is the database key; -the remainder represents the mapped value. -The database key and value may optionally be separated -by the colon character. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl d Ar dbtype -Specify the format of the database. -Available formats are -.Ar hash -and -.Ar btree . -The default value is -.Ar hash . -.It Fl o Ar dbfile -Write the generated database to -.Ar dbfile . -.It Fl t Ar type -Specify the format of the resulting map file. -The default map format is suitable for storing simple, unstructured, -key-to-value string associations. -However, if the mapped value has special meaning, -as in the case of the virtual domains file, -a suitable -.Ar type -must be provided. -The available output types are: -.Bl -tag -width "aliases" -.It Cm aliases -The mapped value is a comma-separated list of mail destinations. -This format can be used for building user aliases and -user mappings for virtual domain files. -.It Cm set -There is no mapped value \(en a map of this type will only allow for -the lookup of keys. -This format can be used for building primary domain maps. -.El -.It Fl U -Instead of generating a database map from text input, -dump the contents of a database map as text -with the key and value separated with a tab. -.El -.Sh PRIMARY DOMAINS -Primary domains can be kept in tables. -To create a primary domain table, add each primary domain on a -single line by itself. -.Pp -In addition to adding an entry to the primary domain map, -one must add a filter rule that accepts mail for the domain -map, for example: -.Bd -literal -offset indent -table domains db:/etc/mail/domains.db - -action "local" mbox - -match for domain <domains> action "local" -.Ed -.Sh VIRTUAL DOMAINS -Virtual domains may also be kept in tables. -To create a virtual domain table, add each virtual domain on a -single line by itself. -.Pp -Virtual domains expect a mapping of virtual users to real users -in order to determine if a recipient is accepted or not. -The mapping format is an extension to -.Xr aliases 5 , -which allows the use of -.Dq user@domain.tld -to accept user only on the specified domain, -.Dq user -to accept the user for any of the virtual domains, -.Dq @domain.tld -to provide a catch-all for the specified domain and -.Dq @ -to provide a global catch-all for all domains. -.Xr smtpd 8 -will perform the lookups in that specific order. -.Pp -To create single virtual address, add -.Dq user@example.com user -to the users map. -To handle all mail destined to any user at example.com, add -.Dq @example.com user -to the virtual map. -.Pp -In addition to adding an entry to the virtual map, -one must add a filter rule that accepts mail for virtual domains, -for example: -.Bd -literal -offset indent -table vdomains db:/etc/mail/vdomains.db -table vusers db:/etc/mail/users.db - -action "local" mbox virtual <vusers> - -match for domain <vdomains> action "local" -match for domain "example.org" action "local" -.Ed -.Sh FILES -.Bl -tag -width "/etc/mail/aliasesXXX" -compact -.It Pa /etc/mail/aliases -List of user mail aliases. -.It Pa /etc/mail/secrets -List of remote host credentials. -.El -.Sh EXIT STATUS -.Ex -std makemap -.Sh SEE ALSO -.Xr aliases 5 , -.Xr smtpd.conf 5 , -.Xr table 5 , -.Xr newaliases 8 , -.Xr smtpd 8 -.Sh HISTORY -The -.Nm -command first appeared in -.Ox 4.6 -as a replacement for the equivalent command shipped with sendmail. diff --git a/smtpd/makemap.c b/smtpd/makemap.c deleted file mode 100644 index 10e3f555..00000000 --- a/smtpd/makemap.c +++ /dev/null @@ -1,521 +0,0 @@ -/* $OpenBSD: makemap.c,v 1.73 2020/02/24 16:16:07 millert Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#ifdef HAVE_SYS_FILE_H -#include <sys/file.h> /* Needed for flock */ -#endif -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/tree.h> -#include <sys/queue.h> -#include <sys/socket.h> - -#include <ctype.h> -#ifdef HAVE_DB_H -#include <db.h> -#elif defined(HAVE_DB1_DB_H) -#include <db1/db.h> -#elif defined(HAVE_DB_185_H) -#include <db_185.h> -#endif -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <unistd.h> -#include <limits.h> -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#ifdef HAVE_LIBUTIL_H -#include <libutil.h> -#endif - -#include "smtpd.h" -#include "log.h" - -#define PATH_ALIASES SMTPD_CONFDIR "/aliases" - -static void usage(void); -static int parse_map(DB *, int *, char *); -static int parse_entry(DB *, int *, char *, size_t, size_t); -static int parse_mapentry(DB *, int *, char *, size_t, size_t); -static int parse_setentry(DB *, int *, char *, size_t, size_t); -static int make_plain(DBT *, char *); -static int make_aliases(DBT *, char *); -static char *conf_aliases(char *); -static int dump_db(const char *, DBTYPE); - -char *source; -static int mode; - -enum output_type { - T_PLAIN, - T_ALIASES, - T_SET -} type; - -/* - * Stub functions so that makemap compiles using minimum object files. - */ -int -fork_proc_backend(const char *backend, const char *conf, const char *procname) -{ - return (-1); -} - -int -makemap(int prog_mode, int argc, char *argv[]) -{ - struct stat sb; - char dbname[PATH_MAX]; - DB *db; - const char *opts; - char *conf, *oflag = NULL; - int ch, dbputs = 0, Uflag = 0; - DBTYPE dbtype = DB_HASH; - char *p; - gid_t gid; - int fd = -1; - - gid = getgid(); - if (setresgid(gid, gid, gid) == -1) - err(1, "setresgid"); - - if ((env = config_default()) == NULL) - err(1, NULL); - - log_init(1, LOG_MAIL); - - mode = prog_mode; - conf = CONF_FILE; - type = T_PLAIN; - opts = "b:C:d:ho:O:t:U"; - if (mode == P_NEWALIASES) - opts = "f:h"; - - while ((ch = getopt(argc, argv, opts)) != -1) { - switch (ch) { - case 'b': - if (optarg && strcmp(optarg, "i") == 0) - mode = P_NEWALIASES; - break; - case 'C': - break; /* for compatibility */ - case 'd': - if (strcmp(optarg, "hash") == 0) - dbtype = DB_HASH; - else if (strcmp(optarg, "btree") == 0) - dbtype = DB_BTREE; - else - errx(1, "unsupported DB type '%s'", optarg); - break; - case 'f': - conf = optarg; - break; - case 'o': - oflag = optarg; - break; - case 'O': - if (strncmp(optarg, "AliasFile=", 10) != 0) - break; - type = T_ALIASES; - p = strchr(optarg, '='); - source = ++p; - break; - case 't': - if (strcmp(optarg, "aliases") == 0) - type = T_ALIASES; - else if (strcmp(optarg, "set") == 0) - type = T_SET; - else - errx(1, "unsupported type '%s'", optarg); - break; - case 'U': - Uflag = 1; - break; - default: - usage(); - } - } - argc -= optind; - argv += optind; - - /* sendmail-compat makemap ... re-execute using proper interface */ - if (argc == 2) { - if (oflag) - usage(); - - p = strstr(argv[1], ".db"); - if (p == NULL || strcmp(p, ".db") != 0) { - if (!bsnprintf(dbname, sizeof dbname, "%s.db", - argv[1])) - errx(1, "database name too long"); - } - else { - if (strlcpy(dbname, argv[1], sizeof dbname) - >= sizeof dbname) - errx(1, "database name too long"); - } - - execl(PATH_MAKEMAP, "makemap", "-d", argv[0], "-o", dbname, - "-", (char *)NULL); - err(1, "execl"); - } - - if (mode == P_NEWALIASES) { - if (geteuid()) - errx(1, "need root privileges"); - if (argc != 0) - usage(); - type = T_ALIASES; - if (source == NULL) - source = conf_aliases(conf); - } else { - if (argc != 1) - usage(); - source = argv[0]; - } - - if (Uflag) - return dump_db(source, dbtype); - - if (oflag == NULL && asprintf(&oflag, "%s.db", source) == -1) - err(1, "asprintf"); - - if (strcmp(source, "-") != 0) - if (stat(source, &sb) == -1) - err(1, "stat: %s", source); - - if (!bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", oflag)) - errx(1, "path too long"); - if ((fd = mkstemp(dbname)) == -1) - err(1, "mkstemp"); - - db = dbopen(dbname, O_TRUNC|O_RDWR, 0644, dbtype, NULL); - if (db == NULL) { - warn("dbopen: %s", dbname); - goto bad; - } - - if (strcmp(source, "-") != 0) - if (fchmod(db->fd(db), sb.st_mode) == -1 || - fchown(db->fd(db), sb.st_uid, sb.st_gid) == -1) { - warn("couldn't carry ownership and perms to %s", - dbname); - goto bad; - } - - if (!parse_map(db, &dbputs, source)) - goto bad; - - if (db->close(db) == -1) { - warn("dbclose: %s", dbname); - goto bad; - } - - /* force to disk before renaming over an existing file */ - if (fsync(fd) == -1) { - warn("fsync: %s", dbname); - goto bad; - } - if (close(fd) == -1) { - fd = -1; - warn("close: %s", dbname); - goto bad; - } - fd = -1; - - if (rename(dbname, oflag) == -1) { - warn("rename"); - goto bad; - } - - if (mode == P_NEWALIASES) - printf("%s: %d aliases\n", source, dbputs); - else if (dbputs == 0) - warnx("warning: empty map created: %s", oflag); - - return 0; -bad: - if (fd != -1) - close(fd); - unlink(dbname); - return 1; -} - -static int -parse_map(DB *db, int *dbputs, char *filename) -{ - FILE *fp; - char *line; - size_t len; - size_t lineno = 0; - - if (strcmp(filename, "-") == 0) - fp = fdopen(0, "r"); - else - fp = fopen(filename, "r"); - if (fp == NULL) { - warn("%s", filename); - return 0; - } - - if (!isatty(fileno(fp)) && flock(fileno(fp), LOCK_SH|LOCK_NB) == -1) { - if (errno == EWOULDBLOCK) - warnx("%s is locked", filename); - else - warn("%s: flock", filename); - fclose(fp); - return 0; - } - - while ((line = fparseln(fp, &len, &lineno, - NULL, FPARSELN_UNESCCOMM)) != NULL) { - if (!parse_entry(db, dbputs, line, len, lineno)) { - free(line); - fclose(fp); - return 0; - } - free(line); - } - - fclose(fp); - return 1; -} - -static int -parse_entry(DB *db, int *dbputs, char *line, size_t len, size_t lineno) -{ - switch (type) { - case T_PLAIN: - case T_ALIASES: - return parse_mapentry(db, dbputs, line, len, lineno); - case T_SET: - return parse_setentry(db, dbputs, line, len, lineno); - } - return 0; -} - -static int -parse_mapentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno) -{ - DBT key; - DBT val; - char *keyp; - char *valp; - - keyp = line; - while (isspace((unsigned char)*keyp)) - keyp++; - if (*keyp == '\0') - return 1; - - valp = keyp; - strsep(&valp, " \t:"); - if (valp == NULL || valp == keyp) - goto bad; - while (*valp == ':' || isspace((unsigned char)*valp)) - valp++; - if (*valp == '\0') - goto bad; - - /* Check for dups. */ - key.data = keyp; - key.size = strlen(keyp) + 1; - - xlowercase(key.data, key.data, strlen(key.data) + 1); - if (db->get(db, &key, &val, 0) == 0) { - warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp); - return 0; - } - - if (type == T_PLAIN) { - if (!make_plain(&val, valp)) - goto bad; - } - else if (type == T_ALIASES) { - if (!make_aliases(&val, valp)) - goto bad; - } - - if (db->put(db, &key, &val, 0) == -1) { - warn("dbput"); - return 0; - } - - (*dbputs)++; - - free(val.data); - - return 1; - -bad: - warnx("%s:%zd: invalid entry", source, lineno); - return 0; -} - -static int -parse_setentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno) -{ - DBT key; - DBT val; - char *keyp; - - keyp = line; - while (isspace((unsigned char)*keyp)) - keyp++; - if (*keyp == '\0') - return 1; - - val.data = "<set>"; - val.size = strlen(val.data) + 1; - - /* Check for dups. */ - key.data = keyp; - key.size = strlen(keyp) + 1; - xlowercase(key.data, key.data, strlen(key.data) + 1); - if (db->get(db, &key, &val, 0) == 0) { - warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp); - return 0; - } - - if (db->put(db, &key, &val, 0) == -1) { - warn("dbput"); - return 0; - } - - (*dbputs)++; - - return 1; -} - -static int -make_plain(DBT *val, char *text) -{ - val->data = xstrdup(text); - val->size = strlen(text) + 1; - - return (val->size); -} - -static int -make_aliases(DBT *val, char *text) -{ - struct expandnode xn; - char *subrcpt; - char *origtext; - - val->data = NULL; - val->size = 0; - - origtext = xstrdup(text); - - while ((subrcpt = strsep(&text, ",")) != NULL) { - /* subrcpt: strip initial and trailing whitespace. */ - subrcpt = strip(subrcpt); - if (*subrcpt == '\0') - goto error; - - if (!text_to_expandnode(&xn, subrcpt)) - goto error; - } - - val->data = origtext; - val->size = strlen(origtext) + 1; - return (val->size); - -error: - free(origtext); - - return 0; -} - -static char * -conf_aliases(char *cfgpath) -{ - struct table *table; - char *path; - char *p; - - if (parse_config(env, cfgpath, 0)) - exit(1); - - table = table_find(env, "aliases"); - if (table == NULL) - return (PATH_ALIASES); - - path = xstrdup(table->t_config); - p = strstr(path, ".db"); - if (p == NULL || strcmp(p, ".db") != 0) { - return (path); - } - *p = '\0'; - return (path); -} - -static int -dump_db(const char *dbname, DBTYPE dbtype) -{ - DB *db; - DBT key, val; - char *keystr, *valstr; - int r; - - db = dbopen(dbname, O_RDONLY, 0644, dbtype, NULL); - if (db == NULL) - err(1, "dbopen: %s", dbname); - - for (r = db->seq(db, &key, &val, R_FIRST); r == 0; - r = db->seq(db, &key, &val, R_NEXT)) { - keystr = key.data; - valstr = val.data; - if (keystr[key.size - 1] == '\0') - key.size--; - if (valstr[val.size - 1] == '\0') - val.size--; - printf("%.*s\t%.*s\n", (int)key.size, keystr, - (int)val.size, valstr); - } - if (r == -1) - err(1, "db->seq: %s", dbname); - - if (db->close(db) == -1) - err(1, "dbclose: %s", dbname); - - return 0; -} - -static void -usage(void) -{ - if (mode == P_NEWALIASES) - fprintf(stderr, "usage: newaliases [-f file]\n"); - else - fprintf(stderr, "usage: makemap [-U] [-d dbtype] [-o dbfile] " - "[-t type] file\n"); - exit(1); -} diff --git a/smtpd/mda.c b/smtpd/mda.c deleted file mode 100644 index 5e8fec19..00000000 --- a/smtpd/mda.c +++ /dev/null @@ -1,919 +0,0 @@ -/* $OpenBSD: mda.c,v 1.141 2019/10/03 08:50:08 gilles Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <grp.h> /* needed for setgroups */ -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> -#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) -#include <vis.h> -#else -#include "bsd-vis.h" -#endif - -#include "smtpd.h" -#include "log.h" - -#define MDA_HIWAT 65536 - -struct mda_envelope { - TAILQ_ENTRY(mda_envelope) entry; - uint64_t session_id; - uint64_t id; - time_t creation; - char *sender; - char *rcpt; - char *dest; - char *user; - char *dispatcher; - char *mda_subaddress; - char *mda_exec; -}; - -#define USER_WAITINFO 0x01 -#define USER_RUNNABLE 0x02 -#define USER_ONHOLD 0x04 -#define USER_HOLDQ 0x08 - -struct mda_user { - uint64_t id; - TAILQ_ENTRY(mda_user) entry; - TAILQ_ENTRY(mda_user) entry_runnable; - char name[LOGIN_NAME_MAX]; - char usertable[PATH_MAX]; - size_t evpcount; - TAILQ_HEAD(, mda_envelope) envelopes; - int flags; - size_t running; - struct userinfo userinfo; -}; - -struct mda_session { - uint64_t id; - struct mda_user *user; - struct mda_envelope *evp; - struct io *io; - FILE *datafp; -}; - -static void mda_io(struct io *, int, void *); -static int mda_check_loop(FILE *, struct mda_envelope *); -static int mda_getlastline(int, char *, size_t); -static void mda_done(struct mda_session *); -static void mda_fail(struct mda_user *, int, const char *, - enum enhanced_status_code); -static void mda_drain(void); -static void mda_log(const struct mda_envelope *, const char *, const char *); -static void mda_queue_ok(uint64_t); -static void mda_queue_tempfail(uint64_t, const char *, - enum enhanced_status_code); -static void mda_queue_permfail(uint64_t, const char *, enum enhanced_status_code); -static void mda_queue_loop(uint64_t); -static struct mda_user *mda_user(const struct envelope *); -static void mda_user_free(struct mda_user *); -static const char *mda_user_to_text(const struct mda_user *); -static struct mda_envelope *mda_envelope(uint64_t, const struct envelope *); -static void mda_envelope_free(struct mda_envelope *); -static struct mda_session * mda_session(struct mda_user *); -static const char *mda_sysexit_to_str(int); - -static struct tree sessions; -static struct tree users; - -static TAILQ_HEAD(, mda_user) runnable; - -void -mda_imsg(struct mproc *p, struct imsg *imsg) -{ - struct mda_session *s; - struct mda_user *u; - struct mda_envelope *e; - struct envelope evp; - struct deliver deliver; - struct msg m; - const void *data; - const char *error, *parent_error, *syserror; - uint64_t reqid; - size_t sz; - char out[256], buf[LINE_MAX]; - int n; - enum lka_resp_status status; - enum mda_resp_status mda_status; - int mda_sysexit; - - switch (imsg->hdr.type) { - case IMSG_MDA_LOOKUP_USERINFO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, (int *)&status); - if (status == LKA_OK) - m_get_data(&m, &data, &sz); - m_end(&m); - - u = tree_xget(&users, reqid); - - if (status == LKA_TEMPFAIL) - mda_fail(u, 0, - "Temporary failure in user lookup", - ESC_OTHER_ADDRESS_STATUS); - else if (status == LKA_PERMFAIL) - mda_fail(u, 1, - "Permanent failure in user lookup", - ESC_DESTINATION_MAILBOX_HAS_MOVED); - else { - if (sz != sizeof(u->userinfo)) - fatalx("mda: userinfo size mismatch"); - memmove(&u->userinfo, data, sz); - u->flags &= ~USER_WAITINFO; - u->flags |= USER_RUNNABLE; - TAILQ_INSERT_TAIL(&runnable, u, entry_runnable); - mda_drain(); - } - return; - - case IMSG_QUEUE_DELIVER: - m_msg(&m, imsg); - m_get_envelope(&m, &evp); - m_end(&m); - - u = mda_user(&evp); - - if (u->evpcount >= env->sc_mda_task_hiwat) { - if (!(u->flags & USER_ONHOLD)) { - log_debug("debug: mda: hiwat reached for " - "user \"%s\": holding envelopes", - mda_user_to_text(u)); - u->flags |= USER_ONHOLD; - } - } - - if (u->flags & USER_ONHOLD) { - u->flags |= USER_HOLDQ; - m_create(p_queue, IMSG_MDA_DELIVERY_HOLD, - 0, 0, -1); - m_add_evpid(p_queue, evp.id); - m_add_id(p_queue, u->id); - m_close(p_queue); - return; - } - - e = mda_envelope(u->id, &evp); - TAILQ_INSERT_TAIL(&u->envelopes, e, entry); - u->evpcount += 1; - stat_increment("mda.pending", 1); - - if (!(u->flags & USER_RUNNABLE) && - !(u->flags & USER_WAITINFO)) { - u->flags |= USER_RUNNABLE; - TAILQ_INSERT_TAIL(&runnable, u, entry_runnable); - } - - mda_drain(); - return; - - case IMSG_MDA_OPEN_MESSAGE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - s = tree_xget(&sessions, reqid); - e = s->evp; - - if (imsg->fd == -1) { - log_debug("debug: mda: cannot get message fd"); - mda_queue_tempfail(e->id, - "Cannot get message fd", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", "Cannot get message fd"); - mda_done(s); - return; - } - - log_debug("debug: mda: got message fd %d " - "for session %016"PRIx64 " evpid %016"PRIx64, - imsg->fd, s->id, e->id); - - if ((s->datafp = fdopen(imsg->fd, "r")) == NULL) { - log_warn("warn: mda: fdopen"); - close(imsg->fd); - mda_queue_tempfail(e->id, "fdopen failed", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", "fdopen failed"); - mda_done(s); - return; - } - - /* check delivery loop */ - if (mda_check_loop(s->datafp, e)) { - log_debug("debug: mda: loop detected"); - mda_queue_loop(e->id); - mda_log(e, "PermFail", "Loop detected"); - mda_done(s); - return; - } - - /* start queueing delivery headers */ - if (e->sender[0]) - /* - * XXX: remove existing Return-Path, - * if any - */ - n = io_printf(s->io, - "Return-Path: <%s>\n" - "Delivered-To: %s\n", - e->sender, - e->rcpt ? e->rcpt : e->dest); - else - n = io_printf(s->io, - "Delivered-To: %s\n", - e->rcpt ? e->rcpt : e->dest); - if (n == -1) { - log_warn("warn: mda: " - "fail to write delivery info"); - mda_queue_tempfail(e->id, "Out of memory", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", "Out of memory"); - mda_done(s); - return; - } - - /* request parent to fork a helper process */ - memset(&deliver, 0, sizeof deliver); - (void)text_to_mailaddr(&deliver.sender, s->evp->sender); - (void)text_to_mailaddr(&deliver.rcpt, s->evp->rcpt); - (void)text_to_mailaddr(&deliver.dest, s->evp->dest); - if (s->evp->mda_exec) - (void)strlcpy(deliver.mda_exec, s->evp->mda_exec, sizeof deliver.mda_exec); - if (s->evp->mda_subaddress) - (void)strlcpy(deliver.mda_subaddress, s->evp->mda_subaddress, sizeof deliver.mda_subaddress); - (void)strlcpy(deliver.dispatcher, s->evp->dispatcher, sizeof deliver.dispatcher); - deliver.userinfo = s->user->userinfo; - - log_debug("debug: mda: querying mda fd " - "for session %016"PRIx64 " evpid %016"PRIx64, - s->id, s->evp->id); - - m_create(p_parent, IMSG_MDA_FORK, 0, 0, -1); - m_add_id(p_parent, reqid); - m_add_data(p_parent, &deliver, sizeof(deliver)); - m_close(p_parent); - return; - - case IMSG_MDA_FORK: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - s = tree_xget(&sessions, reqid); - e = s->evp; - if (imsg->fd == -1) { - log_warn("warn: mda: fail to retrieve mda fd"); - mda_queue_tempfail(e->id, "Cannot get mda fd", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", "Cannot get mda fd"); - mda_done(s); - return; - } - - log_debug("debug: mda: got mda fd %d " - "for session %016"PRIx64 " evpid %016"PRIx64, - imsg->fd, s->id, s->evp->id); - - io_set_nonblocking(imsg->fd); - io_set_fd(s->io, imsg->fd); - io_set_write(s->io); - return; - - case IMSG_MDA_DONE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, (int *)&mda_status); - m_get_int(&m, (int *)&mda_sysexit); - m_get_string(&m, &parent_error); - m_end(&m); - - s = tree_xget(&sessions, reqid); - e = s->evp; - /* - * Grab last line of mda stdout/stderr if available. - */ - out[0] = '\0'; - if (imsg->fd != -1) - mda_getlastline(imsg->fd, out, sizeof(out)); - - /* - * Choose between parent's description of error and - * child's output, the latter having preference over - * the former. - */ - error = NULL; - if (mda_status == MDA_OK) { - if (s->datafp || (s->io && io_queued(s->io))) { - error = "mda exited prematurely"; - mda_status = MDA_TEMPFAIL; - } - } else - error = out[0] ? out : parent_error; - - syserror = NULL; - if (mda_sysexit) - syserror = mda_sysexit_to_str(mda_sysexit); - - /* update queue entry */ - switch (mda_status) { - case MDA_TEMPFAIL: - mda_queue_tempfail(e->id, error, - ESC_OTHER_MAIL_SYSTEM_STATUS); - (void)snprintf(buf, sizeof buf, - "Error (%s%s%s)", - syserror ? syserror : "", - syserror ? ": " : "", - error); - mda_log(e, "TempFail", buf); - break; - case MDA_PERMFAIL: - mda_queue_permfail(e->id, error, - ESC_OTHER_MAIL_SYSTEM_STATUS); - (void)snprintf(buf, sizeof buf, - "Error (%s%s%s)", - syserror ? syserror : "", - syserror ? ": " : "", - error); - mda_log(e, "PermFail", buf); - break; - case MDA_OK: - mda_queue_ok(e->id); - mda_log(e, "Ok", "Delivered"); - break; - } - mda_done(s); - return; - } - - errx(1, "mda_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -void -mda_postfork() -{ -} - -void -mda_postprivdrop() -{ - tree_init(&sessions); - tree_init(&users); - TAILQ_INIT(&runnable); -} - -static void -mda_io(struct io *io, int evt, void *arg) -{ - struct mda_session *s = arg; - char *ln = NULL; - size_t sz = 0; - ssize_t len; - - log_trace(TRACE_IO, "mda: %p: %s %s", s, io_strevent(evt), - io_strio(io)); - - switch (evt) { - case IO_LOWAT: - - /* done */ - done: - if (s->datafp == NULL) { - log_debug("debug: mda: all data sent for session" - " %016"PRIx64 " evpid %016"PRIx64, - s->id, s->evp->id); - io_free(io); - s->io = NULL; - return; - } - - while (io_queued(s->io) < MDA_HIWAT) { - if ((len = getline(&ln, &sz, s->datafp)) == -1) - break; - if (io_write(s->io, ln, len) == -1) { - m_create(p_parent, IMSG_MDA_KILL, - 0, 0, -1); - m_add_id(p_parent, s->id); - m_add_string(p_parent, "Out of memory"); - m_close(p_parent); - io_pause(io, IO_OUT); - free(ln); - return; - } - } - - free(ln); - ln = NULL; - if (ferror(s->datafp)) { - log_debug("debug: mda: ferror on session %016"PRIx64, - s->id); - m_create(p_parent, IMSG_MDA_KILL, 0, 0, -1); - m_add_id(p_parent, s->id); - m_add_string(p_parent, "Error reading body"); - m_close(p_parent); - io_pause(io, IO_OUT); - return; - } - - if (feof(s->datafp)) { - log_debug("debug: mda: end-of-file for session" - " %016"PRIx64 " evpid %016"PRIx64, - s->id, s->evp->id); - fclose(s->datafp); - s->datafp = NULL; - if (io_queued(s->io) == 0) - goto done; - } - return; - - case IO_TIMEOUT: - log_debug("debug: mda: timeout on session %016"PRIx64, s->id); - io_pause(io, IO_OUT); - return; - - case IO_ERROR: - log_debug("debug: mda: io error on session %016"PRIx64": %s", - s->id, io_error(io)); - io_pause(io, IO_OUT); - return; - - case IO_DISCONNECTED: - log_debug("debug: mda: io disconnected on session %016"PRIx64, - s->id); - io_pause(io, IO_OUT); - return; - - default: - log_debug("debug: mda: unexpected event on session %016"PRIx64, - s->id); - io_pause(io, IO_OUT); - return; - } -} - -static int -mda_check_loop(FILE *fp, struct mda_envelope *e) -{ - char *buf = NULL; - size_t sz = 0; - ssize_t len; - int ret = 0; - - while ((len = getline(&buf, &sz, fp)) != -1) { - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - - if (strchr(buf, ':') == NULL && !isspace((unsigned char)*buf)) - break; - - if (strncasecmp("Delivered-To: ", buf, 14) == 0) { - if (strcasecmp(buf + 14, e->dest) == 0) { - ret = 1; - break; - } - } - } - - free(buf); - fseek(fp, SEEK_SET, 0); - return (ret); -} - -static int -mda_getlastline(int fd, char *dst, size_t dstsz) -{ - FILE *fp; - char *ln = NULL; - size_t sz = 0; - ssize_t len; - int out = 0; - - if (lseek(fd, 0, SEEK_SET) < 0) { - log_warn("warn: mda: lseek"); - close(fd); - return (-1); - } - fp = fdopen(fd, "r"); - if (fp == NULL) { - log_warn("warn: mda: fdopen"); - close(fd); - return (-1); - } - while ((len = getline(&ln, &sz, fp)) != -1) { - if (ln[len - 1] == '\n') - ln[len - 1] = '\0'; - out = 1; - } - fclose(fp); - - if (out) { - (void)strlcpy(dst, "\"", dstsz); - (void)strnvis(dst + 1, ln, dstsz - 2, VIS_SAFE | VIS_CSTYLE | VIS_NL); - (void)strlcat(dst, "\"", dstsz); - } - - free(ln); - return (0); -} - -static void -mda_fail(struct mda_user *user, int permfail, const char *error, - enum enhanced_status_code code) -{ - struct mda_envelope *e; - - while ((e = TAILQ_FIRST(&user->envelopes))) { - TAILQ_REMOVE(&user->envelopes, e, entry); - if (permfail) { - mda_log(e, "PermFail", error); - mda_queue_permfail(e->id, error, code); - } - else { - mda_log(e, "TempFail", error); - mda_queue_tempfail(e->id, error, code); - } - mda_envelope_free(e); - } - - mda_user_free(user); -} - -static void -mda_drain(void) -{ - struct mda_user *u; - - while ((u = (TAILQ_FIRST(&runnable)))) { - - TAILQ_REMOVE(&runnable, u, entry_runnable); - - if (u->evpcount == 0 && u->running == 0) { - log_debug("debug: mda: all done for user \"%s\"", - mda_user_to_text(u)); - mda_user_free(u); - continue; - } - - if (u->evpcount == 0) { - log_debug("debug: mda: no more envelope for \"%s\"", - mda_user_to_text(u)); - u->flags &= ~USER_RUNNABLE; - continue; - } - - if (u->running >= env->sc_mda_max_user_session) { - log_debug("debug: mda: " - "maximum number of session reached for user \"%s\"", - mda_user_to_text(u)); - u->flags &= ~USER_RUNNABLE; - continue; - } - - if (tree_count(&sessions) >= env->sc_mda_max_session) { - log_debug("debug: mda: " - "maximum number of session reached"); - TAILQ_INSERT_HEAD(&runnable, u, entry_runnable); - return; - } - - mda_session(u); - - if (u->evpcount == env->sc_mda_task_lowat) { - if (u->flags & USER_ONHOLD) { - log_debug("debug: mda: down to lowat for user " - "\"%s\": releasing", - mda_user_to_text(u)); - u->flags &= ~USER_ONHOLD; - } - if (u->flags & USER_HOLDQ) { - m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE, - 0, 0, -1); - m_add_id(p_queue, u->id); - m_add_int(p_queue, env->sc_mda_task_release); - m_close(p_queue); - } - } - - /* re-add the user at the tail of the queue */ - TAILQ_INSERT_TAIL(&runnable, u, entry_runnable); - } -} - -static void -mda_done(struct mda_session *s) -{ - log_debug("debug: mda: session %016" PRIx64 " done", s->id); - - tree_xpop(&sessions, s->id); - - mda_envelope_free(s->evp); - - s->user->running--; - if (!(s->user->flags & USER_RUNNABLE)) { - log_debug("debug: mda: user \"%s\" becomes runnable", - s->user->name); - TAILQ_INSERT_TAIL(&runnable, s->user, entry_runnable); - s->user->flags |= USER_RUNNABLE; - } - - if (s->datafp) - fclose(s->datafp); - if (s->io) - io_free(s->io); - - free(s); - - stat_decrement("mda.running", 1); - - mda_drain(); -} - -static void -mda_log(const struct mda_envelope *evp, const char *prefix, const char *status) -{ - char rcpt[LINE_MAX]; - - rcpt[0] = '\0'; - if (evp->rcpt) - (void)snprintf(rcpt, sizeof rcpt, "rcpt=<%s> ", evp->rcpt); - - log_info("%016"PRIx64" mda delivery evpid=%016" PRIx64 " from=<%s> to=<%s> " - "%suser=%s delay=%s result=%s stat=%s", - evp->session_id, - evp->id, - evp->sender ? evp->sender : "", - evp->dest, - rcpt, - evp->user, - duration_to_text(time(NULL) - evp->creation), - prefix, - status); -} - -static void -mda_queue_ok(uint64_t evpid) -{ - m_create(p_queue, IMSG_MDA_DELIVERY_OK, 0, 0, -1); - m_add_evpid(p_queue, evpid); - m_close(p_queue); -} - -static void -mda_queue_tempfail(uint64_t evpid, const char *reason, - enum enhanced_status_code code) -{ - m_create(p_queue, IMSG_MDA_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_evpid(p_queue, evpid); - m_add_string(p_queue, reason); - m_add_int(p_queue, (int)code); - m_close(p_queue); -} - -static void -mda_queue_permfail(uint64_t evpid, const char *reason, - enum enhanced_status_code code) -{ - m_create(p_queue, IMSG_MDA_DELIVERY_PERMFAIL, 0, 0, -1); - m_add_evpid(p_queue, evpid); - m_add_string(p_queue, reason); - m_add_int(p_queue, (int)code); - m_close(p_queue); -} - -static void -mda_queue_loop(uint64_t evpid) -{ - m_create(p_queue, IMSG_MDA_DELIVERY_LOOP, 0, 0, -1); - m_add_evpid(p_queue, evpid); - m_close(p_queue); -} - -static struct mda_user * -mda_user(const struct envelope *evp) -{ - struct dispatcher *dsp; - struct mda_user *u; - void *i; - - i = NULL; - dsp = dict_xget(env->sc_dispatchers, evp->dispatcher); - while (tree_iter(&users, &i, NULL, (void**)(&u))) { - if (!strcmp(evp->mda_user, u->name) && - !strcmp(dsp->u.local.table_userbase, u->usertable)) - return (u); - } - - u = xcalloc(1, sizeof *u); - u->id = generate_uid(); - TAILQ_INIT(&u->envelopes); - (void)strlcpy(u->name, evp->mda_user, sizeof(u->name)); - (void)strlcpy(u->usertable, dsp->u.local.table_userbase, - sizeof(u->usertable)); - - tree_xset(&users, u->id, u); - - m_create(p_lka, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1); - m_add_id(p_lka, u->id); - m_add_string(p_lka, dsp->u.local.table_userbase); - m_add_string(p_lka, evp->mda_user); - m_close(p_lka); - u->flags |= USER_WAITINFO; - - stat_increment("mda.user", 1); - - if (dsp->u.local.user) - log_debug("mda: new user %016" PRIx64 - " for \"%s\" delivering as \"%s\"", - u->id, mda_user_to_text(u), dsp->u.local.user); - else - log_debug("mda: new user %016" PRIx64 - " for \"%s\"", u->id, mda_user_to_text(u)); - - return (u); -} - -static void -mda_user_free(struct mda_user *u) -{ - tree_xpop(&users, u->id); - - if (u->flags & USER_HOLDQ) { - m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE, 0, 0, -1); - m_add_id(p_queue, u->id); - m_add_int(p_queue, 0); - m_close(p_queue); - } - - free(u); - stat_decrement("mda.user", 1); -} - -static const char * -mda_user_to_text(const struct mda_user *u) -{ - static char buf[1024]; - - (void)snprintf(buf, sizeof(buf), "%s:%s", u->usertable, u->name); - - return (buf); -} - -static struct mda_envelope * -mda_envelope(uint64_t session_id, const struct envelope *evp) -{ - struct mda_envelope *e; - char buf[LINE_MAX]; - - e = xcalloc(1, sizeof *e); - e->session_id = session_id; - e->id = evp->id; - e->creation = evp->creation; - buf[0] = '\0'; - if (evp->sender.user[0] && evp->sender.domain[0]) - (void)snprintf(buf, sizeof buf, "%s@%s", - evp->sender.user, evp->sender.domain); - e->sender = xstrdup(buf); - (void)snprintf(buf, sizeof buf, "%s@%s", evp->dest.user, - evp->dest.domain); - e->dest = xstrdup(buf); - (void)snprintf(buf, sizeof buf, "%s@%s", evp->rcpt.user, - evp->rcpt.domain); - e->rcpt = xstrdup(buf); - e->user = evp->mda_user[0] ? - xstrdup(evp->mda_user) : xstrdup(evp->dest.user); - e->dispatcher = xstrdup(evp->dispatcher); - if (evp->mda_exec[0]) - e->mda_exec = xstrdup(evp->mda_exec); - if (evp->mda_subaddress[0]) - e->mda_subaddress = xstrdup(evp->mda_subaddress); - stat_increment("mda.envelope", 1); - return (e); -} - -static void -mda_envelope_free(struct mda_envelope *e) -{ - free(e->sender); - free(e->dest); - free(e->rcpt); - free(e->user); - free(e->mda_exec); - free(e); - - stat_decrement("mda.envelope", 1); -} - -static struct mda_session * -mda_session(struct mda_user * u) -{ - struct mda_session *s; - - s = xcalloc(1, sizeof *s); - s->id = generate_uid(); - s->user = u; - s->io = io_new(); - io_set_callback(s->io, mda_io, s); - - tree_xset(&sessions, s->id, s); - - s->evp = TAILQ_FIRST(&u->envelopes); - TAILQ_REMOVE(&u->envelopes, s->evp, entry); - u->evpcount--; - u->running++; - - stat_decrement("mda.pending", 1); - stat_increment("mda.running", 1); - - log_debug("debug: mda: new session %016" PRIx64 - " for user \"%s\" evpid %016" PRIx64, s->id, - mda_user_to_text(u), s->evp->id); - - m_create(p_queue, IMSG_MDA_OPEN_MESSAGE, 0, 0, -1); - m_add_id(p_queue, s->id); - m_add_msgid(p_queue, evpid_to_msgid(s->evp->id)); - m_close(p_queue); - - return (s); -} - -static const char * -mda_sysexit_to_str(int sysexit) -{ - switch (sysexit) { - case EX_USAGE: - return "command line usage error"; - case EX_DATAERR: - return "data format error"; - case EX_NOINPUT: - return "cannot open input"; - case EX_NOUSER: - return "user unknown"; - case EX_NOHOST: - return "host name unknown"; - case EX_UNAVAILABLE: - return "service unavailable"; - case EX_SOFTWARE: - return "internal software error"; - case EX_OSERR: - return "system resource problem"; - case EX_OSFILE: - return "critical OS file missing"; - case EX_CANTCREAT: - return "can't create user output file"; - case EX_IOERR: - return "input/output error"; - case EX_TEMPFAIL: - return "temporary failure"; - case EX_PROTOCOL: - return "remote error in protocol"; - case EX_NOPERM: - return "permission denied"; - case EX_CONFIG: - return "local configuration error"; - default: - break; - } - return NULL; -} - diff --git a/smtpd/mda_mbox.c b/smtpd/mda_mbox.c deleted file mode 100644 index 8918e3ee..00000000 --- a/smtpd/mda_mbox.c +++ /dev/null @@ -1,94 +0,0 @@ -/* $OpenBSD: mda_mbox.c,v 1.2 2020/02/03 15:41:22 gilles Exp $ */ - -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" - - -void -mda_mbox(struct deliver *deliver) -{ - int ret; - char sender[LINE_MAX]; - char *envp[] = { - "HOME=/", - "PATH=" _PATH_DEFPATH, - "LOGNAME=root", - "USER=root", - NULL, - }; - - if (deliver->sender.user[0] == '\0' && - deliver->sender.domain[0] == '\0') - ret = snprintf(sender, sizeof sender, "MAILER-DAEMON"); - else - ret = snprintf(sender, sizeof sender, "%s@%s", - deliver->sender.user, deliver->sender.domain); - if (ret < 0 || (size_t)ret >= sizeof sender) - errx(EX_TEMPFAIL, "sender address too long"); - - execle(PATH_MAILLOCAL, PATH_MAILLOCAL, "-f", - sender, deliver->userinfo.username, (char *)NULL, envp); - perror("execl"); - _exit(EX_TEMPFAIL); -} - -void -mda_mbox_init(struct deliver *deliver) -{ - int fd; - int ret; - char buffer[LINE_MAX]; - - ret = snprintf(buffer, sizeof buffer, "%s/%s", - _PATH_MAILDIR, deliver->userinfo.username); - if (ret < 0 || (size_t)ret >= sizeof buffer) - errx(EX_TEMPFAIL, "mailbox pathname too long"); - - if ((fd = open(buffer, O_CREAT|O_EXCL, 0)) == -1) { - if (errno == EEXIST) - return; - err(EX_TEMPFAIL, "open"); - } - - if (fchown(fd, deliver->userinfo.uid, deliver->userinfo.gid) == -1) - err(EX_TEMPFAIL, "fchown"); - - if (fchmod(fd, S_IRUSR|S_IWUSR) == -1) - err(EX_TEMPFAIL, "fchown"); -} diff --git a/smtpd/mda_unpriv.c b/smtpd/mda_unpriv.c deleted file mode 100644 index 2143b9a0..00000000 --- a/smtpd/mda_unpriv.c +++ /dev/null @@ -1,110 +0,0 @@ -/* $OpenBSD: mda_unpriv.c,v 1.6 2020/02/02 22:13:48 gilles Exp $ */ - -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" - - -void -mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, - const char *pw_name, const char *pw_dir) -{ - int idx; - char *mda_environ[11]; - char mda_exec[LINE_MAX]; - char mda_wrapper[LINE_MAX]; - const char *mda_command; - const char *mda_command_wrap; - - if (deliver->mda_exec[0]) - mda_command = deliver->mda_exec; - else - mda_command = dsp->u.local.command; - - if (strlcpy(mda_exec, mda_command, sizeof (mda_exec)) - >= sizeof (mda_exec)) - errx(1, "mda command line too long"); - - if (mda_expand_format(mda_exec, sizeof mda_exec, deliver, - &deliver->userinfo, NULL) == -1) - errx(1, "mda command line could not be expanded"); - - mda_command = mda_exec; - - /* setup environment similar to other MTA */ - idx = 0; - xasprintf(&mda_environ[idx++], "PATH=%s", _PATH_DEFPATH); - xasprintf(&mda_environ[idx++], "DOMAIN=%s", deliver->rcpt.domain); - xasprintf(&mda_environ[idx++], "HOME=%s", pw_dir); - xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", deliver->dest.user, deliver->dest.domain); - xasprintf(&mda_environ[idx++], "SHELL=/bin/sh"); - xasprintf(&mda_environ[idx++], "LOCAL=%s", deliver->rcpt.user); - xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name); - xasprintf(&mda_environ[idx++], "USER=%s", pw_name); - - if (deliver->sender.user[0]) - xasprintf(&mda_environ[idx++], "SENDER=%s@%s", - deliver->sender.user, deliver->sender.domain); - else - xasprintf(&mda_environ[idx++], "SENDER="); - - if (deliver->mda_subaddress[0]) - xasprintf(&mda_environ[idx++], "EXTENSION=%s", deliver->mda_subaddress); - - mda_environ[idx++] = (char *)NULL; - - if (dsp->u.local.mda_wrapper) { - mda_command_wrap = dict_get(env->sc_mda_wrappers, - dsp->u.local.mda_wrapper); - if (mda_command_wrap == NULL) - errx(1, "could not find wrapper %s", - dsp->u.local.mda_wrapper); - - if (strlcpy(mda_wrapper, mda_command_wrap, sizeof (mda_wrapper)) - >= sizeof (mda_wrapper)) - errx(1, "mda command line too long"); - - if (mda_expand_format(mda_wrapper, sizeof mda_wrapper, deliver, - &deliver->userinfo, mda_command) == -1) - errx(1, "mda command line could not be expanded"); - mda_command = mda_wrapper; - } - execle("/bin/sh", "/bin/sh", "-c", mda_command, (char *)NULL, - mda_environ); - - perror("execle"); - _exit(1); -} - diff --git a/smtpd/mda_variables.c b/smtpd/mda_variables.c deleted file mode 100644 index b672e492..00000000 --- a/smtpd/mda_variables.c +++ /dev/null @@ -1,374 +0,0 @@ -/* $OpenBSD: mda_variables.c,v 1.6 2019/09/19 07:35:36 gilles Exp $ */ - -/* - * Copyright (c) 2011-2017 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define EXPAND_DEPTH 10 - -ssize_t mda_expand_format(char *, size_t, const struct deliver *, - const struct userinfo *, const char *); -static ssize_t mda_expand_token(char *, size_t, const char *, - const struct deliver *, const struct userinfo *, const char *); -static int mod_lowercase(char *, size_t); -static int mod_uppercase(char *, size_t); -static int mod_strip(char *, size_t); - -static struct modifiers { - char *name; - int (*f)(char *buf, size_t len); -} token_modifiers[] = { - { "lowercase", mod_lowercase }, - { "uppercase", mod_uppercase }, - { "strip", mod_strip }, - { "raw", NULL }, /* special case, must stay last */ -}; - -#define MAXTOKENLEN 128 - -static ssize_t -mda_expand_token(char *dest, size_t len, const char *token, - const struct deliver *dlv, const struct userinfo *ui, const char *mda_command) -{ - char rtoken[MAXTOKENLEN]; - char tmp[EXPAND_BUFFER]; - const char *string; - char *lbracket, *rbracket, *content, *sep, *mods; - ssize_t i; - ssize_t begoff, endoff; - const char *errstr = NULL; - int replace = 1; - int raw = 0; - - begoff = 0; - endoff = EXPAND_BUFFER; - mods = NULL; - - if (strlcpy(rtoken, token, sizeof rtoken) >= sizeof rtoken) - return -1; - - /* token[x[:y]] -> extracts optional x and y, converts into offsets */ - if ((lbracket = strchr(rtoken, '[')) && - (rbracket = strchr(rtoken, ']'))) { - /* ] before [ ... or empty */ - if (rbracket < lbracket || rbracket - lbracket <= 1) - return -1; - - *lbracket = *rbracket = '\0'; - content = lbracket + 1; - - if ((sep = strchr(content, ':')) == NULL) - endoff = begoff = strtonum(content, -EXPAND_BUFFER, - EXPAND_BUFFER, &errstr); - else { - *sep = '\0'; - if (content != sep) - begoff = strtonum(content, -EXPAND_BUFFER, - EXPAND_BUFFER, &errstr); - if (*(++sep)) { - if (errstr == NULL) - endoff = strtonum(sep, -EXPAND_BUFFER, - EXPAND_BUFFER, &errstr); - } - } - if (errstr) - return -1; - - /* token:mod_1,mod_2,mod_n -> extract modifiers */ - mods = strchr(rbracket + 1, ':'); - } else { - if ((mods = strchr(rtoken, ':')) != NULL) - *mods++ = '\0'; - } - - /* token -> expanded token */ - if (!strcasecmp("sender", rtoken)) { - if (snprintf(tmp, sizeof tmp, "%s@%s", - dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp) - return -1; - if (strcmp(tmp, "@") == 0) - (void)strlcpy(tmp, "", sizeof tmp); - string = tmp; - } - else if (!strcasecmp("rcpt", rtoken)) { - if (snprintf(tmp, sizeof tmp, "%s@%s", - dlv->rcpt.user, dlv->rcpt.domain) >= (int)sizeof tmp) - return -1; - if (strcmp(tmp, "@") == 0) - (void)strlcpy(tmp, "", sizeof tmp); - string = tmp; - } - else if (!strcasecmp("dest", rtoken)) { - if (snprintf(tmp, sizeof tmp, "%s@%s", - dlv->dest.user, dlv->dest.domain) >= (int)sizeof tmp) - return -1; - if (strcmp(tmp, "@") == 0) - (void)strlcpy(tmp, "", sizeof tmp); - string = tmp; - } - else if (!strcasecmp("sender.user", rtoken)) - string = dlv->sender.user; - else if (!strcasecmp("sender.domain", rtoken)) - string = dlv->sender.domain; - else if (!strcasecmp("user.username", rtoken)) - string = ui->username; - else if (!strcasecmp("user.directory", rtoken)) { - string = ui->directory; - replace = 0; - } - else if (!strcasecmp("rcpt.user", rtoken)) - string = dlv->rcpt.user; - else if (!strcasecmp("rcpt.domain", rtoken)) - string = dlv->rcpt.domain; - else if (!strcasecmp("dest.user", rtoken)) - string = dlv->dest.user; - else if (!strcasecmp("dest.domain", rtoken)) - string = dlv->dest.domain; - else if (!strcasecmp("mda", rtoken)) { - string = mda_command; - replace = 0; - } - else if (!strcasecmp("mbox.from", rtoken)) { - if (snprintf(tmp, sizeof tmp, "%s@%s", - dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp) - return -1; - if (strcmp(tmp, "@") == 0) - (void)strlcpy(tmp, "MAILER-DAEMON", sizeof tmp); - string = tmp; - } - else - return -1; - - if (string != tmp) { - if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp) - return -1; - string = tmp; - } - - /* apply modifiers */ - if (mods != NULL) { - do { - if ((sep = strchr(mods, '|')) != NULL) - *sep++ = '\0'; - for (i = 0; (size_t)i < nitems(token_modifiers); ++i) { - if (!strcasecmp(token_modifiers[i].name, mods)) { - if (token_modifiers[i].f == NULL) { - raw = 1; - break; - } - if (!token_modifiers[i].f(tmp, sizeof tmp)) - return -1; /* modifier error */ - break; - } - } - if ((size_t)i == nitems(token_modifiers)) - return -1; /* modifier not found */ - } while ((mods = sep) != NULL); - } - - if (!raw && replace) - for (i = 0; (size_t)i < strlen(tmp); ++i) - if (strchr(MAILADDR_ESCAPE, tmp[i])) - tmp[i] = ':'; - - /* expanded string is empty */ - i = strlen(string); - if (i == 0) - return 0; - - /* begin offset beyond end of string */ - if (begoff >= i) - return -1; - - /* end offset beyond end of string, make it end of string */ - if (endoff >= i) - endoff = i - 1; - - /* negative begin offset, make it relative to end of string */ - if (begoff < 0) - begoff += i; - /* negative end offset, make it relative to end of string, - * note that end offset is inclusive. - */ - if (endoff < 0) - endoff += i - 1; - - /* check that final offsets are valid */ - if (begoff < 0 || endoff < 0 || endoff < begoff) - return -1; - endoff += 1; /* end offset is inclusive */ - - /* check that substring does not exceed destination buffer length */ - i = endoff - begoff; - if ((size_t)i + 1 >= len) - return -1; - - string += begoff; - for (; i; i--) { - *dest = *string; - dest++; - string++; - } - - return endoff - begoff; -} - - -ssize_t -mda_expand_format(char *buf, size_t len, const struct deliver *dlv, - const struct userinfo *ui, const char *mda_command) -{ - char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; - char exptok[EXPAND_BUFFER]; - ssize_t exptoklen; - char token[MAXTOKENLEN]; - size_t ret, tmpret; - - if (len < sizeof tmpbuf) { - log_warnx("mda_expand_format: tmp buffer < rule buffer"); - return -1; - } - - memset(tmpbuf, 0, sizeof tmpbuf); - pbuf = buf; - ptmp = tmpbuf; - ret = tmpret = 0; - - /* special case: ~/ only allowed expanded at the beginning */ - if (strncmp(pbuf, "~/", 2) == 0) { - tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory); - if (tmpret >= sizeof tmpbuf) { - log_warnx("warn: user directory for %s too large", - ui->directory); - return 0; - } - ret += tmpret; - ptmp += tmpret; - pbuf += 2; - } - - - /* expansion loop */ - for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) { - if (*pbuf == '%' && *(pbuf + 1) == '%') { - *ptmp++ = *pbuf++; - pbuf += 1; - tmpret = 1; - continue; - } - - if (*pbuf != '%' || *(pbuf + 1) != '{') { - *ptmp++ = *pbuf++; - tmpret = 1; - continue; - } - - /* %{...} otherwise fail */ - if (*(pbuf+1) != '{' || (ebuf = strchr(pbuf+1, '}')) == NULL) - return 0; - - /* extract token from %{token} */ - if ((size_t)(ebuf - pbuf) - 1 >= sizeof token) - return 0; - - memcpy(token, pbuf+2, ebuf-pbuf-1); - if (strchr(token, '}') == NULL) - return 0; - *strchr(token, '}') = '\0'; - - exptoklen = mda_expand_token(exptok, sizeof exptok, token, dlv, - ui, mda_command); - if (exptoklen == -1) - return -1; - - /* writing expanded token at ptmp will overflow tmpbuf */ - if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= (size_t)exptoklen) - return -1; - - memcpy(ptmp, exptok, exptoklen); - pbuf = ebuf + 1; - ptmp += exptoklen; - tmpret = exptoklen; - } - if (ret >= sizeof tmpbuf) - return -1; - - if ((ret = strlcpy(buf, tmpbuf, len)) >= len) - return -1; - - return ret; -} - -static int -mod_lowercase(char *buf, size_t len) -{ - char tmp[EXPAND_BUFFER]; - - if (!lowercase(tmp, buf, sizeof tmp)) - return 0; - if (strlcpy(buf, tmp, len) >= len) - return 0; - return 1; -} - -static int -mod_uppercase(char *buf, size_t len) -{ - char tmp[EXPAND_BUFFER]; - - if (!uppercase(tmp, buf, sizeof tmp)) - return 0; - if (strlcpy(buf, tmp, len) >= len) - return 0; - return 1; -} - -static int -mod_strip(char *buf, size_t len) -{ - char *tag, *at; - unsigned int i; - - /* gilles+hackers -> gilles */ - if ((tag = strchr(buf, *env->sc_subaddressing_delim)) != NULL) { - /* gilles+hackers@poolp.org -> gilles@poolp.org */ - if ((at = strchr(tag, '@')) != NULL) { - for (i = 0; i <= strlen(at); ++i) - tag[i] = at[i]; - } else - *tag = '\0'; - } - return 1; -} diff --git a/smtpd/mproc.c b/smtpd/mproc.c deleted file mode 100644 index bde229e1..00000000 --- a/smtpd/mproc.c +++ /dev/null @@ -1,676 +0,0 @@ -/* $OpenBSD: mproc.c,v 1.36 2020/03/17 09:01:53 tobhe Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@faurot.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/queue.h> -#include <sys/uio.h> - -#include <netinet/in.h> -#include <arpa/inet.h> -#include <arpa/nameser.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -static void mproc_dispatch(int, short, void *); - -static ssize_t imsg_read_nofd(struct imsgbuf *); - -int -mproc_fork(struct mproc *p, const char *path, char *argv[]) -{ - int sp[2]; - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) - return (-1); - - io_set_nonblocking(sp[0]); - io_set_nonblocking(sp[1]); - - if ((p->pid = fork()) == -1) - goto err; - - if (p->pid == 0) { - /* child process */ - dup2(sp[0], STDIN_FILENO); - closefrom(STDERR_FILENO + 1); - execv(path, argv); - err(1, "execv: %s", path); - } - - /* parent process */ - close(sp[0]); - mproc_init(p, sp[1]); - return (0); - -err: - log_warn("warn: Failed to start process %s, instance of %s", argv[0], path); - close(sp[0]); - close(sp[1]); - return (-1); -} - -void -mproc_init(struct mproc *p, int fd) -{ - imsg_init(&p->imsgbuf, fd); -} - -void -mproc_clear(struct mproc *p) -{ - log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid); - - event_del(&p->ev); - close(p->imsgbuf.fd); - imsg_clear(&p->imsgbuf); -} - -void -mproc_enable(struct mproc *p) -{ - if (p->enable == 0) { - log_trace(TRACE_MPROC, "mproc: %s -> %s: enabled", - proc_name(smtpd_process), - proc_name(p->proc)); - p->enable = 1; - } - mproc_event_add(p); -} - -void -mproc_disable(struct mproc *p) -{ - if (p->enable == 1) { - log_trace(TRACE_MPROC, "mproc: %s -> %s: disabled", - proc_name(smtpd_process), - proc_name(p->proc)); - p->enable = 0; - } - mproc_event_add(p); -} - -void -mproc_event_add(struct mproc *p) -{ - short events; - - if (p->enable) - events = EV_READ; - else - events = 0; - - if (p->imsgbuf.w.queued) - events |= EV_WRITE; - - if (p->events) - event_del(&p->ev); - - p->events = events; - if (events) { - event_set(&p->ev, p->imsgbuf.fd, events, mproc_dispatch, p); - event_add(&p->ev, NULL); - } -} - -static void -mproc_dispatch(int fd, short event, void *arg) -{ - struct mproc *p = arg; - struct imsg imsg; - ssize_t n; - - p->events = 0; - - if (event & EV_READ) { - - if (p->proc == PROC_CLIENT) - n = imsg_read_nofd(&p->imsgbuf); - else - n = imsg_read(&p->imsgbuf); - - switch (n) { - case -1: - if (errno == EAGAIN) - break; - log_warn("warn: %s -> %s: imsg_read", - proc_name(smtpd_process), p->name); - fatal("exiting"); - /* NOTREACHED */ - case 0: - /* this pipe is dead, so remove the event handler */ - log_debug("debug: %s -> %s: pipe closed", - proc_name(smtpd_process), p->name); - p->handler(p, NULL); - return; - default: - break; - } - } - - if (event & EV_WRITE) { - n = msgbuf_write(&p->imsgbuf.w); - if (n == 0 || (n == -1 && errno != EAGAIN)) { - /* this pipe is dead, so remove the event handler */ - log_debug("debug: %s -> %s: pipe closed", - proc_name(smtpd_process), p->name); - p->handler(p, NULL); - return; - } - } - - for (;;) { - if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { - - if (smtpd_process == PROC_CONTROL && - p->proc == PROC_CLIENT) { - log_warnx("warn: client sent invalid imsg " - "over control socket"); - p->handler(p, NULL); - return; - } - log_warn("fatal: %s: error in imsg_get for %s", - proc_name(smtpd_process), p->name); - fatalx(NULL); - } - if (n == 0) - break; - - p->handler(p, &imsg); - - imsg_free(&imsg); - } - - mproc_event_add(p); -} - -/* This should go into libutil */ -static ssize_t -imsg_read_nofd(struct imsgbuf *ibuf) -{ - ssize_t n; - char *buf; - size_t len; - - buf = ibuf->r.buf + ibuf->r.wpos; - len = sizeof(ibuf->r.buf) - ibuf->r.wpos; - - while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { - if (errno != EINTR) - return (n); - } - - ibuf->r.wpos += n; - return (n); -} - -void -m_forward(struct mproc *p, struct imsg *imsg) -{ - imsg_compose(&p->imsgbuf, imsg->hdr.type, imsg->hdr.peerid, - imsg->hdr.pid, imsg->fd, imsg->data, - imsg->hdr.len - sizeof(imsg->hdr)); - - if (imsg->hdr.type != IMSG_STAT_DECREMENT && - imsg->hdr.type != IMSG_STAT_INCREMENT) - log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (forward)", - proc_name(smtpd_process), - proc_name(p->proc), - imsg->hdr.len - sizeof(imsg->hdr), - imsg_to_str(imsg->hdr.type)); - - mproc_event_add(p); -} - -void -m_compose(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, - void *data, size_t len) -{ - imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len); - - if (type != IMSG_STAT_DECREMENT && - type != IMSG_STAT_INCREMENT) - log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", - proc_name(smtpd_process), - proc_name(p->proc), - len, - imsg_to_str(type)); - - mproc_event_add(p); -} - -void -m_composev(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, - int fd, const struct iovec *iov, int n) -{ - size_t len; - int i; - - imsg_composev(&p->imsgbuf, type, peerid, pid, fd, iov, n); - - len = 0; - for (i = 0; i < n; i++) - len += iov[i].iov_len; - - if (type != IMSG_STAT_DECREMENT && - type != IMSG_STAT_INCREMENT) - log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", - proc_name(smtpd_process), - proc_name(p->proc), - len, - imsg_to_str(type)); - - mproc_event_add(p); -} - -void -m_create(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) -{ - p->m_pos = 0; - p->m_type = type; - p->m_peerid = peerid; - p->m_pid = pid; - p->m_fd = fd; -} - -void -m_add(struct mproc *p, const void *data, size_t len) -{ - size_t alloc; - void *tmp; - - if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) { - log_warnx("warn: message too large"); - fatal(NULL); - } - - alloc = p->m_alloc ? p->m_alloc : 128; - while (p->m_pos + len > alloc) - alloc *= 2; - if (alloc != p->m_alloc) { - log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu", - proc_name(smtpd_process), - proc_name(p->proc), - p->m_alloc, - alloc); - - tmp = recallocarray(p->m_buf, p->m_alloc, alloc, 1); - if (tmp == NULL) - fatal("realloc"); - p->m_alloc = alloc; - p->m_buf = tmp; - } - - memmove(p->m_buf + p->m_pos, data, len); - p->m_pos += len; -} - -void -m_close(struct mproc *p) -{ - if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, - p->m_buf, p->m_pos) == -1) - fatal("imsg_compose"); - - log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", - proc_name(smtpd_process), - proc_name(p->proc), - p->m_pos, - imsg_to_str(p->m_type)); - - mproc_event_add(p); -} - -void -m_flush(struct mproc *p) -{ - if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, - p->m_buf, p->m_pos) == -1) - fatal("imsg_compose"); - - log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)", - proc_name(smtpd_process), - proc_name(p->proc), - p->m_pos, - imsg_to_str(p->m_type)); - - p->m_pos = 0; - - if (imsg_flush(&p->imsgbuf) == -1) - fatal("imsg_flush"); -} - -static struct imsg * current; - -static void -m_error(const char *error) -{ - char buf[512]; - - (void)snprintf(buf, sizeof buf, "%s: %s: %s", - proc_name(smtpd_process), - imsg_to_str(current->hdr.type), - error); - fatalx("%s", buf); -} - -void -m_msg(struct msg *m, struct imsg *imsg) -{ - current = imsg; - m->pos = imsg->data; - m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr)); -} - -void -m_end(struct msg *m) -{ - if (m->pos != m->end) - m_error("not at msg end"); -} - -int -m_is_eom(struct msg *m) -{ - return (m->pos == m->end); -} - -static inline void -m_get(struct msg *m, void *dst, size_t sz) -{ - if (sz > MAX_IMSGSIZE || - m->end - m->pos < (ssize_t)sz) - fatalx("msg too short"); - - memmove(dst, m->pos, sz); - m->pos += sz; -} - -void -m_add_int(struct mproc *m, int v) -{ - m_add(m, &v, sizeof(v)); -}; - -void -m_add_u32(struct mproc *m, uint32_t u32) -{ - m_add(m, &u32, sizeof(u32)); -}; - -void -m_add_size(struct mproc *m, size_t sz) -{ - m_add(m, &sz, sizeof(sz)); -}; - -void -m_add_time(struct mproc *m, time_t v) -{ - m_add(m, &v, sizeof(v)); -}; - -void -m_add_timeval(struct mproc *m, struct timeval *tv) -{ - m_add(m, tv, sizeof(*tv)); -} - - -void -m_add_string(struct mproc *m, const char *v) -{ - if (v) { - m_add(m, "s", 1); - m_add(m, v, strlen(v) + 1); - } - else - m_add(m, "\0", 1); -}; - -void -m_add_data(struct mproc *m, const void *v, size_t len) -{ - m_add_size(m, len); - m_add(m, v, len); -}; - -void -m_add_id(struct mproc *m, uint64_t v) -{ - m_add(m, &v, sizeof(v)); -} - -void -m_add_evpid(struct mproc *m, uint64_t v) -{ - m_add(m, &v, sizeof(v)); -} - -void -m_add_msgid(struct mproc *m, uint32_t v) -{ - m_add(m, &v, sizeof(v)); -} - -void -m_add_sockaddr(struct mproc *m, const struct sockaddr *sa) -{ - m_add_size(m, SA_LEN(sa)); - m_add(m, sa, SA_LEN(sa)); -} - -void -m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr) -{ - m_add(m, maddr, sizeof(*maddr)); -} - -void -m_add_envelope(struct mproc *m, const struct envelope *evp) -{ - char buf[sizeof(*evp)]; - - envelope_dump_buffer(evp, buf, sizeof(buf)); - m_add_evpid(m, evp->id); - m_add_string(m, buf); -} - -void -m_add_params(struct mproc *m, struct dict *d) -{ - const char *key; - char *value; - void *iter; - - if (d == NULL) { - m_add_size(m, 0); - return; - } - m_add_size(m, dict_count(d)); - iter = NULL; - while (dict_iter(d, &iter, &key, (void **)&value)) { - m_add_string(m, key); - m_add_string(m, value); - } -} - -void -m_get_int(struct msg *m, int *i) -{ - m_get(m, i, sizeof(*i)); -} - -void -m_get_u32(struct msg *m, uint32_t *u32) -{ - m_get(m, u32, sizeof(*u32)); -} - -void -m_get_size(struct msg *m, size_t *sz) -{ - m_get(m, sz, sizeof(*sz)); -} - -void -m_get_time(struct msg *m, time_t *t) -{ - m_get(m, t, sizeof(*t)); -} - -void -m_get_timeval(struct msg *m, struct timeval *tv) -{ - m_get(m, tv, sizeof(*tv)); -} - -void -m_get_string(struct msg *m, const char **s) -{ - uint8_t *end; - char c; - - if (m->pos >= m->end) - m_error("msg too short"); - - c = *m->pos++; - if (c == '\0') { - *s = NULL; - return; - } - - if (m->pos >= m->end) - m_error("msg too short"); - end = memchr(m->pos, 0, m->end - m->pos); - if (end == NULL) - m_error("unterminated string"); - - *s = m->pos; - m->pos = end + 1; -} - -void -m_get_data(struct msg *m, const void **data, size_t *sz) -{ - m_get_size(m, sz); - - if (*sz == 0) { - *data = NULL; - return; - } - - if (m->pos + *sz > m->end) - m_error("msg too short"); - - *data = m->pos; - m->pos += *sz; -} - -void -m_get_evpid(struct msg *m, uint64_t *evpid) -{ - m_get(m, evpid, sizeof(*evpid)); -} - -void -m_get_msgid(struct msg *m, uint32_t *msgid) -{ - m_get(m, msgid, sizeof(*msgid)); -} - -void -m_get_id(struct msg *m, uint64_t *id) -{ - m_get(m, id, sizeof(*id)); -} - -void -m_get_sockaddr(struct msg *m, struct sockaddr *sa) -{ - size_t len; - - m_get_size(m, &len); - m_get(m, sa, len); -} - -void -m_get_mailaddr(struct msg *m, struct mailaddr *maddr) -{ - m_get(m, maddr, sizeof(*maddr)); -} - -void -m_get_envelope(struct msg *m, struct envelope *evp) -{ - uint64_t evpid; - const char *buf; - - m_get_evpid(m, &evpid); - m_get_string(m, &buf); - if (buf == NULL) - fatalx("empty envelope buffer"); - - if (!envelope_load_buffer(evp, buf, strlen(buf))) - fatalx("failed to retrieve envelope"); - evp->id = evpid; -} - -void -m_get_params(struct msg *m, struct dict *d) -{ - size_t c; - const char *key; - const char *value; - char *tmp; - - dict_init(d); - - m_get_size(m, &c); - - for (; c; c--) { - m_get_string(m, &key); - m_get_string(m, &value); - if ((tmp = strdup(value)) == NULL) - fatal("m_get_params"); - dict_set(d, key, tmp); - } -} - -void -m_clear_params(struct dict *d) -{ - char *value; - - while (dict_poproot(d, (void **)&value)) - free(value); -} diff --git a/smtpd/mta.c b/smtpd/mta.c deleted file mode 100644 index 922170ae..00000000 --- a/smtpd/mta.c +++ /dev/null @@ -1,2647 +0,0 @@ -/* $OpenBSD: mta.c,v 1.234 2019/12/21 10:34:07 gilles Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <netdb.h> -#include <grp.h> /* needed for setgroups */ -#include <limits.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -#define MAXERROR_PER_ROUTE 4 - -#define DELAY_CHECK_SOURCE 1 -#define DELAY_CHECK_SOURCE_SLOW 10 -#define DELAY_CHECK_SOURCE_FAST 0 -#define DELAY_CHECK_LIMIT 5 - -#define DELAY_QUADRATIC 1 -#define DELAY_ROUTE_BASE 15 -#define DELAY_ROUTE_MAX 3600 - -#define RELAY_ONHOLD 0x01 -#define RELAY_HOLDQ 0x02 - -static void mta_handle_envelope(struct envelope *, const char *); -static void mta_query_smarthost(struct envelope *); -static void mta_on_smarthost(struct envelope *, const char *); -static void mta_query_mx(struct mta_relay *); -static void mta_query_secret(struct mta_relay *); -static void mta_query_preference(struct mta_relay *); -static void mta_query_source(struct mta_relay *); -static void mta_on_mx(void *, void *, void *); -static void mta_on_secret(struct mta_relay *, const char *); -static void mta_on_preference(struct mta_relay *, int); -static void mta_on_source(struct mta_relay *, struct mta_source *); -static void mta_on_timeout(struct runq *, void *); -static void mta_connect(struct mta_connector *); -static void mta_route_enable(struct mta_route *); -static void mta_route_disable(struct mta_route *, int, int); -static void mta_drain(struct mta_relay *); -static void mta_delivery_flush_event(int, short, void *); -static void mta_flush(struct mta_relay *, int, const char *); -static struct mta_route *mta_find_route(struct mta_connector *, time_t, int*, - time_t*, struct mta_mx **); -static void mta_log(const struct mta_envelope *, const char *, const char *, - const char *, const char *); - -SPLAY_HEAD(mta_relay_tree, mta_relay); -static struct mta_relay *mta_relay(struct envelope *, struct relayhost *); -static void mta_relay_ref(struct mta_relay *); -static void mta_relay_unref(struct mta_relay *); -static void mta_relay_show(struct mta_relay *, struct mproc *, uint32_t, time_t); -static int mta_relay_cmp(const struct mta_relay *, const struct mta_relay *); -SPLAY_PROTOTYPE(mta_relay_tree, mta_relay, entry, mta_relay_cmp); - -SPLAY_HEAD(mta_host_tree, mta_host); -static struct mta_host *mta_host(const struct sockaddr *); -static void mta_host_ref(struct mta_host *); -static void mta_host_unref(struct mta_host *); -static int mta_host_cmp(const struct mta_host *, const struct mta_host *); -SPLAY_PROTOTYPE(mta_host_tree, mta_host, entry, mta_host_cmp); - -SPLAY_HEAD(mta_domain_tree, mta_domain); -static struct mta_domain *mta_domain(char *, int); -#if 0 -static void mta_domain_ref(struct mta_domain *); -#endif -static void mta_domain_unref(struct mta_domain *); -static int mta_domain_cmp(const struct mta_domain *, const struct mta_domain *); -SPLAY_PROTOTYPE(mta_domain_tree, mta_domain, entry, mta_domain_cmp); - -SPLAY_HEAD(mta_source_tree, mta_source); -static struct mta_source *mta_source(const struct sockaddr *); -static void mta_source_ref(struct mta_source *); -static void mta_source_unref(struct mta_source *); -static const char *mta_source_to_text(struct mta_source *); -static int mta_source_cmp(const struct mta_source *, const struct mta_source *); -SPLAY_PROTOTYPE(mta_source_tree, mta_source, entry, mta_source_cmp); - -static struct mta_connector *mta_connector(struct mta_relay *, - struct mta_source *); -static void mta_connector_free(struct mta_connector *); -static const char *mta_connector_to_text(struct mta_connector *); - -SPLAY_HEAD(mta_route_tree, mta_route); -static struct mta_route *mta_route(struct mta_source *, struct mta_host *); -static void mta_route_ref(struct mta_route *); -static void mta_route_unref(struct mta_route *); -static const char *mta_route_to_text(struct mta_route *); -static int mta_route_cmp(const struct mta_route *, const struct mta_route *); -SPLAY_PROTOTYPE(mta_route_tree, mta_route, entry, mta_route_cmp); - -struct mta_block { - SPLAY_ENTRY(mta_block) entry; - struct mta_source *source; - char *domain; -}; - -SPLAY_HEAD(mta_block_tree, mta_block); -void mta_block(struct mta_source *, char *); -void mta_unblock(struct mta_source *, char *); -int mta_is_blocked(struct mta_source *, char *); -static int mta_block_cmp(const struct mta_block *, const struct mta_block *); -SPLAY_PROTOTYPE(mta_block_tree, mta_block, entry, mta_block_cmp); - -static struct mta_relay_tree relays; -static struct mta_domain_tree domains; -static struct mta_host_tree hosts; -static struct mta_source_tree sources; -static struct mta_route_tree routes; -static struct mta_block_tree blocks; - -static struct tree wait_mx; -static struct tree wait_preference; -static struct tree wait_secret; -static struct tree wait_smarthost; -static struct tree wait_source; -static struct tree flush_evp; -static struct event ev_flush_evp; - -static struct runq *runq_relay; -static struct runq *runq_connector; -static struct runq *runq_route; -static struct runq *runq_hoststat; - -static time_t max_seen_conndelay_route; -static time_t max_seen_discdelay_route; - -#define HOSTSTAT_EXPIRE_DELAY (4 * 3600) -struct hoststat { - char name[HOST_NAME_MAX+1]; - time_t tm; - char error[LINE_MAX]; - struct tree deferred; -}; -static struct dict hoststat; - -void mta_hoststat_update(const char *, const char *); -void mta_hoststat_cache(const char *, uint64_t); -void mta_hoststat_uncache(const char *, uint64_t); -void mta_hoststat_reschedule(const char *); -static void mta_hoststat_remove_entry(struct hoststat *); - -void -mta_imsg(struct mproc *p, struct imsg *imsg) -{ - struct mta_relay *relay; - struct mta_domain *domain; - struct mta_host *host; - struct mta_route *route; - struct mta_block *block; - struct mta_mx *mx, *imx; - struct mta_source *source; - struct hoststat *hs; - struct sockaddr_storage ss; - struct envelope evp, *e; - struct msg m; - const char *secret; - const char *hostname; - const char *dom; - const char *smarthost; - uint64_t reqid; - time_t t; - char buf[LINE_MAX]; - int dnserror, preference, v, status; - void *iter; - uint64_t u64; - - switch (imsg->hdr.type) { - case IMSG_QUEUE_TRANSFER: - m_msg(&m, imsg); - m_get_envelope(&m, &evp); - m_end(&m); - mta_handle_envelope(&evp, NULL); - return; - - case IMSG_MTA_OPEN_MESSAGE: - mta_session_imsg(p, imsg); - return; - - case IMSG_MTA_LOOKUP_CREDENTIALS: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &secret); - m_end(&m); - relay = tree_xpop(&wait_secret, reqid); - mta_on_secret(relay, secret[0] ? secret : NULL); - return; - - case IMSG_MTA_LOOKUP_SOURCE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &status); - if (status == LKA_OK) - m_get_sockaddr(&m, (struct sockaddr*)&ss); - m_end(&m); - - relay = tree_xpop(&wait_source, reqid); - mta_on_source(relay, (status == LKA_OK) ? - mta_source((struct sockaddr *)&ss) : NULL); - return; - - case IMSG_MTA_LOOKUP_SMARTHOST: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &status); - smarthost = NULL; - if (status == LKA_OK) - m_get_string(&m, &smarthost); - m_end(&m); - - e = tree_xpop(&wait_smarthost, reqid); - mta_on_smarthost(e, smarthost); - return; - - case IMSG_MTA_LOOKUP_HELO: - mta_session_imsg(p, imsg); - return; - - case IMSG_MTA_DNS_HOST: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &hostname); - m_get_sockaddr(&m, (struct sockaddr*)&ss); - m_get_int(&m, &preference); - m_end(&m); - domain = tree_xget(&wait_mx, reqid); - mx = xcalloc(1, sizeof *mx); - mx->mxname = xstrdup(hostname); - mx->host = mta_host((struct sockaddr*)&ss); - mx->preference = preference; - TAILQ_FOREACH(imx, &domain->mxs, entry) { - if (imx->preference > mx->preference) { - TAILQ_INSERT_BEFORE(imx, mx, entry); - return; - } - } - TAILQ_INSERT_TAIL(&domain->mxs, mx, entry); - return; - - case IMSG_MTA_DNS_HOST_END: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &dnserror); - m_end(&m); - domain = tree_xpop(&wait_mx, reqid); - domain->mxstatus = dnserror; - if (domain->mxstatus == DNS_OK) { - log_debug("debug: MXs for domain %s:", - domain->name); - TAILQ_FOREACH(mx, &domain->mxs, entry) - log_debug(" %s preference %d", - sa_to_text(mx->host->sa), - mx->preference); - } - else { - log_debug("debug: Failed MX query for %s:", - domain->name); - } - domain->lastmxquery = time(NULL); - waitq_run(&domain->mxs, domain); - return; - - case IMSG_MTA_DNS_MX_PREFERENCE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &dnserror); - if (dnserror == 0) - m_get_int(&m, &preference); - m_end(&m); - - relay = tree_xpop(&wait_preference, reqid); - if (dnserror) { - log_warnx("warn: Couldn't find backup " - "preference for %s: error %d", - mta_relay_to_text(relay), dnserror); - preference = INT_MAX; - } - mta_on_preference(relay, preference); - return; - - case IMSG_CTL_RESUME_ROUTE: - u64 = *((uint64_t *)imsg->data); - if (u64) - log_debug("resuming route: %llu", - (unsigned long long)u64); - else - log_debug("resuming all routes"); - SPLAY_FOREACH(route, mta_route_tree, &routes) { - if (u64 && route->id != u64) - continue; - - if (route->flags & ROUTE_DISABLED) { - log_info("smtp-out: Enabling route %s per admin request", - mta_route_to_text(route)); - if (!runq_cancel(runq_route, route)) { - log_warnx("warn: route not on runq"); - fatalx("exiting"); - } - route->flags &= ~ROUTE_DISABLED; - route->flags |= ROUTE_NEW; - route->nerror = 0; - route->penalty = 0; - mta_route_unref(route); /* from mta_route_disable */ - } - - if (u64) - break; - } - return; - - case IMSG_CTL_MTA_SHOW_HOSTS: - t = time(NULL); - SPLAY_FOREACH(host, mta_host_tree, &hosts) { - (void)snprintf(buf, sizeof(buf), - "%s %s refcount=%d nconn=%zu lastconn=%s", - sockaddr_to_text(host->sa), - host->ptrname, - host->refcount, - host->nconn, - host->lastconn ? duration_to_text(t - host->lastconn) : "-"); - m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS, - imsg->hdr.peerid, 0, -1, - buf, strlen(buf) + 1); - } - m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_CTL_MTA_SHOW_RELAYS: - t = time(NULL); - SPLAY_FOREACH(relay, mta_relay_tree, &relays) - mta_relay_show(relay, p, imsg->hdr.peerid, t); - m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_CTL_MTA_SHOW_ROUTES: - SPLAY_FOREACH(route, mta_route_tree, &routes) { - v = runq_pending(runq_route, route, &t); - (void)snprintf(buf, sizeof(buf), - "%llu. %s %c%c%c%c nconn=%zu nerror=%d penalty=%d timeout=%s", - (unsigned long long)route->id, - mta_route_to_text(route), - route->flags & ROUTE_NEW ? 'N' : '-', - route->flags & ROUTE_DISABLED ? 'D' : '-', - route->flags & ROUTE_RUNQ ? 'Q' : '-', - route->flags & ROUTE_KEEPALIVE ? 'K' : '-', - route->nconn, - route->nerror, - route->penalty, - v ? duration_to_text(t - time(NULL)) : "-"); - m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, - imsg->hdr.peerid, 0, -1, - buf, strlen(buf) + 1); - } - m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_CTL_MTA_SHOW_HOSTSTATS: - iter = NULL; - while (dict_iter(&hoststat, &iter, &hostname, - (void **)&hs)) { - (void)snprintf(buf, sizeof(buf), - "%s|%llu|%s", - hostname, (unsigned long long) hs->tm, - hs->error); - m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS, - imsg->hdr.peerid, 0, -1, - buf, strlen(buf) + 1); - } - m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS, - imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_CTL_MTA_BLOCK: - m_msg(&m, imsg); - m_get_sockaddr(&m, (struct sockaddr*)&ss); - m_get_string(&m, &dom); - m_end(&m); - source = mta_source((struct sockaddr*)&ss); - if (*dom != '\0') { - if (!(strlcpy(buf, dom, sizeof(buf)) - >= sizeof(buf))) - mta_block(source, buf); - } - else - mta_block(source, NULL); - mta_source_unref(source); - m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL, 0); - return; - - case IMSG_CTL_MTA_UNBLOCK: - m_msg(&m, imsg); - m_get_sockaddr(&m, (struct sockaddr*)&ss); - m_get_string(&m, &dom); - m_end(&m); - source = mta_source((struct sockaddr*)&ss); - if (*dom != '\0') { - if (!(strlcpy(buf, dom, sizeof(buf)) - >= sizeof(buf))) - mta_unblock(source, buf); - } - else - mta_unblock(source, NULL); - mta_source_unref(source); - m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL, 0); - return; - - case IMSG_CTL_MTA_SHOW_BLOCK: - SPLAY_FOREACH(block, mta_block_tree, &blocks) { - (void)snprintf(buf, sizeof(buf), "%s -> %s", - mta_source_to_text(block->source), - block->domain ? block->domain : "*"); - m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK, - imsg->hdr.peerid, 0, -1, buf, strlen(buf) + 1); - } - m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - } - - errx(1, "mta_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -void -mta_postfork(void) -{ -} - -void -mta_postprivdrop(void) -{ - SPLAY_INIT(&relays); - SPLAY_INIT(&domains); - SPLAY_INIT(&hosts); - SPLAY_INIT(&sources); - SPLAY_INIT(&routes); - SPLAY_INIT(&blocks); - - tree_init(&wait_secret); - tree_init(&wait_smarthost); - tree_init(&wait_mx); - tree_init(&wait_preference); - tree_init(&wait_source); - tree_init(&flush_evp); - dict_init(&hoststat); - - evtimer_set(&ev_flush_evp, mta_delivery_flush_event, NULL); - - runq_init(&runq_relay, mta_on_timeout); - runq_init(&runq_connector, mta_on_timeout); - runq_init(&runq_route, mta_on_timeout); - runq_init(&runq_hoststat, mta_on_timeout); -} - - -/* - * Local error on the given source. - */ -void -mta_source_error(struct mta_relay *relay, struct mta_route *route, const char *e) -{ - struct mta_connector *c; - - /* - * Remember the source as broken for this connector. - */ - c = mta_connector(relay, route->src); - if (!(c->flags & CONNECTOR_ERROR_SOURCE)) - log_info("smtp-out: Error on %s: %s", - mta_route_to_text(route), e); - c->flags |= CONNECTOR_ERROR_SOURCE; -} - -void -mta_route_error(struct mta_relay *relay, struct mta_route *route) -{ -#if 0 - route->nerror += 1; - - if (route->nerror > MAXERROR_PER_ROUTE) { - log_info("smtp-out: Too many errors on %s: " - "disabling for a while", mta_route_to_text(route)); - mta_route_disable(route, 2, ROUTE_DISABLED_SMTP); - } -#endif -} - -void -mta_route_ok(struct mta_relay *relay, struct mta_route *route) -{ - struct mta_connector *c; - - if (!(route->flags & ROUTE_NEW)) - return; - - log_debug("debug: mta-routing: route %s is now valid.", - mta_route_to_text(route)); - - route->nerror = 0; - route->flags &= ~ROUTE_NEW; - - c = mta_connector(relay, route->src); - mta_connect(c); -} - -void -mta_route_down(struct mta_relay *relay, struct mta_route *route) -{ -#if 0 - mta_route_disable(route, 2, ROUTE_DISABLED_SMTP); -#endif -} - -void -mta_route_collect(struct mta_relay *relay, struct mta_route *route) -{ - struct mta_connector *c; - - log_debug("debug: mta_route_collect(%s)", - mta_route_to_text(route)); - - relay->nconn -= 1; - relay->domain->nconn -= 1; - route->nconn -= 1; - route->src->nconn -= 1; - route->dst->nconn -= 1; - route->lastdisc = time(NULL); - - /* First connection failed */ - if (route->flags & ROUTE_NEW) - mta_route_disable(route, 1, ROUTE_DISABLED_NET); - - c = mta_connector(relay, route->src); - c->nconn -= 1; - mta_connect(c); - mta_route_unref(route); /* from mta_find_route() */ - mta_relay_unref(relay); /* from mta_connect() */ -} - -struct mta_task * -mta_route_next_task(struct mta_relay *relay, struct mta_route *route) -{ - struct mta_task *task; - - if ((task = TAILQ_FIRST(&relay->tasks))) { - TAILQ_REMOVE(&relay->tasks, task, entry); - relay->ntask -= 1; - task->relay = NULL; - - /* When the number of tasks is down to lowat, query some evp */ - if (relay->ntask == (size_t)relay->limits->task_lowat) { - if (relay->state & RELAY_ONHOLD) { - log_info("smtp-out: back to lowat on %s: releasing", - mta_relay_to_text(relay)); - relay->state &= ~RELAY_ONHOLD; - } - if (relay->state & RELAY_HOLDQ) { - m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); - m_add_id(p_queue, relay->id); - m_add_int(p_queue, relay->limits->task_release); - m_close(p_queue); - } - } - else if (relay->ntask == 0 && relay->state & RELAY_HOLDQ) { - m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); - m_add_id(p_queue, relay->id); - m_add_int(p_queue, 0); - m_close(p_queue); - } - } - - return (task); -} - -static void -mta_handle_envelope(struct envelope *evp, const char *smarthost) -{ - struct mta_relay *relay; - struct mta_task *task; - struct mta_envelope *e; - struct dispatcher *dispatcher; - struct mailaddr maddr; - struct relayhost relayh; - char buf[LINE_MAX]; - - dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher); - if (dispatcher->u.remote.smarthost && smarthost == NULL) { - mta_query_smarthost(evp); - return; - } - - memset(&relayh, 0, sizeof(relayh)); - relayh.tls = RELAY_TLS_OPPORTUNISTIC; - if (smarthost && !text_to_relayhost(&relayh, smarthost)) { - log_warnx("warn: Failed to parse smarthost %s", smarthost); - m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_evpid(p_queue, evp->id); - m_add_string(p_queue, "Cannot parse smarthost"); - m_add_int(p_queue, ESC_OTHER_STATUS); - m_close(p_queue); - return; - } - - if (relayh.flags & RELAY_AUTH && dispatcher->u.remote.auth == NULL) { - log_warnx("warn: No auth table on action \"%s\" for relay %s", - evp->dispatcher, smarthost); - m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_evpid(p_queue, evp->id); - m_add_string(p_queue, "No auth table for relaying"); - m_add_int(p_queue, ESC_OTHER_STATUS); - m_close(p_queue); - return; - } - - if (dispatcher->u.remote.tls_required) { - /* Reject relay if smtp+notls:// is requested */ - if (relayh.tls == RELAY_TLS_NO) { - log_warnx("warn: TLS required for action \"%s\"", - evp->dispatcher); - m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_evpid(p_queue, evp->id); - m_add_string(p_queue, "TLS required for relaying"); - m_add_int(p_queue, ESC_OTHER_STATUS); - m_close(p_queue); - return; - } - /* Update smtp:// to smtp+tls:// */ - if (relayh.tls == RELAY_TLS_OPPORTUNISTIC) - relayh.tls = RELAY_TLS_STARTTLS; - } - - relay = mta_relay(evp, &relayh); - /* ignore if we don't know the limits yet */ - if (relay->limits && - relay->ntask >= (size_t)relay->limits->task_hiwat) { - if (!(relay->state & RELAY_ONHOLD)) { - log_info("smtp-out: hiwat reached on %s: holding envelopes", - mta_relay_to_text(relay)); - relay->state |= RELAY_ONHOLD; - } - } - - /* - * If the relay has too many pending tasks, tell the - * scheduler to hold it until further notice - */ - if (relay->state & RELAY_ONHOLD) { - relay->state |= RELAY_HOLDQ; - m_create(p_queue, IMSG_MTA_DELIVERY_HOLD, 0, 0, -1); - m_add_evpid(p_queue, evp->id); - m_add_id(p_queue, relay->id); - m_close(p_queue); - mta_relay_unref(relay); /* from here */ - return; - } - - task = NULL; - TAILQ_FOREACH(task, &relay->tasks, entry) - if (task->msgid == evpid_to_msgid(evp->id)) - break; - - if (task == NULL) { - task = xmalloc(sizeof *task); - TAILQ_INIT(&task->envelopes); - task->relay = relay; - relay->ntask += 1; - TAILQ_INSERT_TAIL(&relay->tasks, task, entry); - task->msgid = evpid_to_msgid(evp->id); - if (evp->sender.user[0] || evp->sender.domain[0]) - (void)snprintf(buf, sizeof buf, "%s@%s", - evp->sender.user, evp->sender.domain); - else - buf[0] = '\0'; - - if (dispatcher->u.remote.mail_from && evp->sender.user[0]) { - memset(&maddr, 0, sizeof (maddr)); - if (text_to_mailaddr(&maddr, - dispatcher->u.remote.mail_from)) { - (void)snprintf(buf, sizeof buf, "%s@%s", - maddr.user[0] ? maddr.user : evp->sender.user, - maddr.domain[0] ? maddr.domain : evp->sender.domain); - } - } - - task->sender = xstrdup(buf); - stat_increment("mta.task", 1); - } - - e = xcalloc(1, sizeof *e); - e->id = evp->id; - e->creation = evp->creation; - e->smtpname = xstrdup(evp->smtpname); - (void)snprintf(buf, sizeof buf, "%s@%s", - evp->dest.user, evp->dest.domain); - e->dest = xstrdup(buf); - (void)snprintf(buf, sizeof buf, "%s@%s", - evp->rcpt.user, evp->rcpt.domain); - if (strcmp(buf, e->dest)) - e->rcpt = xstrdup(buf); - e->task = task; - if (evp->dsn_orcpt.user[0] && evp->dsn_orcpt.domain[0]) { - (void)snprintf(buf, sizeof buf, "%s@%s", - evp->dsn_orcpt.user, evp->dsn_orcpt.domain); - e->dsn_orcpt = xstrdup(buf); - } - (void)strlcpy(e->dsn_envid, evp->dsn_envid, - sizeof e->dsn_envid); - e->dsn_notify = evp->dsn_notify; - e->dsn_ret = evp->dsn_ret; - - TAILQ_INSERT_TAIL(&task->envelopes, e, entry); - log_debug("debug: mta: received evp:%016" PRIx64 - " for <%s>", e->id, e->dest); - - stat_increment("mta.envelope", 1); - - mta_drain(relay); - mta_relay_unref(relay); /* from here */ -} - -static void -mta_delivery_flush_event(int fd, short event, void *arg) -{ - struct mta_envelope *e; - struct timeval tv; - - if (tree_poproot(&flush_evp, NULL, (void**)(&e))) { - - if (e->delivery == IMSG_MTA_DELIVERY_OK) { - m_create(p_queue, IMSG_MTA_DELIVERY_OK, 0, 0, -1); - m_add_evpid(p_queue, e->id); - m_add_int(p_queue, e->ext); - m_close(p_queue); - } else if (e->delivery == IMSG_MTA_DELIVERY_TEMPFAIL) { - m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_evpid(p_queue, e->id); - m_add_string(p_queue, e->status); - m_add_int(p_queue, ESC_OTHER_STATUS); - m_close(p_queue); - } - else if (e->delivery == IMSG_MTA_DELIVERY_PERMFAIL) { - m_create(p_queue, IMSG_MTA_DELIVERY_PERMFAIL, 0, 0, -1); - m_add_evpid(p_queue, e->id); - m_add_string(p_queue, e->status); - m_add_int(p_queue, ESC_OTHER_STATUS); - m_close(p_queue); - } - else if (e->delivery == IMSG_MTA_DELIVERY_LOOP) { - m_create(p_queue, IMSG_MTA_DELIVERY_LOOP, 0, 0, -1); - m_add_evpid(p_queue, e->id); - m_close(p_queue); - } - else { - log_warnx("warn: bad delivery type %d for %016" PRIx64, - e->delivery, e->id); - fatalx("aborting"); - } - - log_debug("debug: mta: flush for %016"PRIx64" (-> %s)", e->id, e->dest); - - free(e->smtpname); - free(e->dest); - free(e->rcpt); - free(e->dsn_orcpt); - free(e); - - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&ev_flush_evp, &tv); - } -} - -void -mta_delivery_log(struct mta_envelope *e, const char *source, const char *relay, - int delivery, const char *status) -{ - if (delivery == IMSG_MTA_DELIVERY_OK) - mta_log(e, "Ok", source, relay, status); - else if (delivery == IMSG_MTA_DELIVERY_TEMPFAIL) - mta_log(e, "TempFail", source, relay, status); - else if (delivery == IMSG_MTA_DELIVERY_PERMFAIL) - mta_log(e, "PermFail", source, relay, status); - else if (delivery == IMSG_MTA_DELIVERY_LOOP) - mta_log(e, "PermFail", source, relay, "Loop detected"); - else { - log_warnx("warn: bad delivery type %d for %016" PRIx64, - delivery, e->id); - fatalx("aborting"); - } - - e->delivery = delivery; - if (status) - (void)strlcpy(e->status, status, sizeof(e->status)); -} - -void -mta_delivery_notify(struct mta_envelope *e) -{ - struct timeval tv; - - tree_xset(&flush_evp, e->id, e); - if (tree_count(&flush_evp) == 1) { - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&ev_flush_evp, &tv); - } -} - -static void -mta_query_mx(struct mta_relay *relay) -{ - uint64_t id; - - if (relay->status & RELAY_WAIT_MX) - return; - - log_debug("debug: mta: querying MX for %s...", - mta_relay_to_text(relay)); - - if (waitq_wait(&relay->domain->mxs, mta_on_mx, relay)) { - id = generate_uid(); - tree_xset(&wait_mx, id, relay->domain); - if (relay->domain->as_host) - m_create(p_lka, IMSG_MTA_DNS_HOST, 0, 0, -1); - else - m_create(p_lka, IMSG_MTA_DNS_MX, 0, 0, -1); - m_add_id(p_lka, id); - m_add_string(p_lka, relay->domain->name); - m_close(p_lka); - } - relay->status |= RELAY_WAIT_MX; - mta_relay_ref(relay); -} - -static void -mta_query_limits(struct mta_relay *relay) -{ - if (relay->status & RELAY_WAIT_LIMITS) - return; - - relay->limits = dict_get(env->sc_limits_dict, relay->domain->name); - if (relay->limits == NULL) - relay->limits = dict_get(env->sc_limits_dict, "default"); - - if (max_seen_conndelay_route < relay->limits->conndelay_route) - max_seen_conndelay_route = relay->limits->conndelay_route; - if (max_seen_discdelay_route < relay->limits->discdelay_route) - max_seen_discdelay_route = relay->limits->discdelay_route; -} - -static void -mta_query_secret(struct mta_relay *relay) -{ - if (relay->status & RELAY_WAIT_SECRET) - return; - - log_debug("debug: mta: querying secret for %s...", - mta_relay_to_text(relay)); - - tree_xset(&wait_secret, relay->id, relay); - relay->status |= RELAY_WAIT_SECRET; - - m_create(p_lka, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1); - m_add_id(p_lka, relay->id); - m_add_string(p_lka, relay->authtable); - m_add_string(p_lka, relay->authlabel); - m_close(p_lka); - - mta_relay_ref(relay); -} - -static void -mta_query_smarthost(struct envelope *evp0) -{ - struct dispatcher *dispatcher; - struct envelope *evp; - - evp = malloc(sizeof(*evp)); - memmove(evp, evp0, sizeof(*evp)); - - dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher); - - log_debug("debug: mta: querying smarthost for %s:%s...", - evp->dispatcher, dispatcher->u.remote.smarthost); - - tree_xset(&wait_smarthost, evp->id, evp); - - m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1); - m_add_id(p_lka, evp->id); - if (dispatcher->u.remote.smarthost_domain) - m_add_string(p_lka, evp->dest.domain); - else - m_add_string(p_lka, NULL); - m_add_string(p_lka, dispatcher->u.remote.smarthost); - m_close(p_lka); - - log_debug("debug: mta: querying smarthost"); -} - -static void -mta_query_preference(struct mta_relay *relay) -{ - if (relay->status & RELAY_WAIT_PREFERENCE) - return; - - log_debug("debug: mta: querying preference for %s...", - mta_relay_to_text(relay)); - - tree_xset(&wait_preference, relay->id, relay); - relay->status |= RELAY_WAIT_PREFERENCE; - - m_create(p_lka, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1); - m_add_id(p_lka, relay->id); - m_add_string(p_lka, relay->domain->name); - m_add_string(p_lka, relay->backupname); - m_close(p_lka); - - mta_relay_ref(relay); -} - -static void -mta_query_source(struct mta_relay *relay) -{ - log_debug("debug: mta: querying source for %s...", - mta_relay_to_text(relay)); - - relay->sourceloop += 1; - - if (relay->sourcetable == NULL) { - /* - * This is a recursive call, but it only happens once, since - * another source will not be queried immediately. - */ - mta_relay_ref(relay); - mta_on_source(relay, mta_source(NULL)); - return; - } - - m_create(p_lka, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1); - m_add_id(p_lka, relay->id); - m_add_string(p_lka, relay->sourcetable); - m_close(p_lka); - - tree_xset(&wait_source, relay->id, relay); - relay->status |= RELAY_WAIT_SOURCE; - mta_relay_ref(relay); -} - -static void -mta_on_mx(void *tag, void *arg, void *data) -{ - struct mta_domain *domain = data; - struct mta_relay *relay = arg; - - log_debug("debug: mta: ... got mx (%p, %s, %s)", - tag, domain->name, mta_relay_to_text(relay)); - - switch (domain->mxstatus) { - case DNS_OK: - break; - case DNS_RETRY: - relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; - relay->failstr = "Temporary failure in MX lookup"; - break; - case DNS_EINVAL: - relay->fail = IMSG_MTA_DELIVERY_PERMFAIL; - relay->failstr = "Invalid domain name"; - break; - case DNS_ENONAME: - relay->fail = IMSG_MTA_DELIVERY_PERMFAIL; - relay->failstr = "Domain does not exist"; - break; - case DNS_ENOTFOUND: - relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; - if (relay->domain->as_host) - relay->failstr = "Host not found"; - else - relay->failstr = "No MX found for domain"; - break; - default: - fatalx("bad DNS lookup error code"); - break; - } - - if (domain->mxstatus) - log_info("smtp-out: Failed to resolve MX for %s: %s", - mta_relay_to_text(relay), relay->failstr); - - relay->status &= ~RELAY_WAIT_MX; - mta_drain(relay); - mta_relay_unref(relay); /* from mta_drain() */ -} - -static void -mta_on_secret(struct mta_relay *relay, const char *secret) -{ - log_debug("debug: mta: ... got secret for %s: %s", - mta_relay_to_text(relay), secret); - - if (secret) - relay->secret = strdup(secret); - - if (relay->secret == NULL) { - log_warnx("warn: Failed to retrieve secret " - "for %s", mta_relay_to_text(relay)); - relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; - relay->failstr = "Could not retrieve credentials"; - } - - relay->status &= ~RELAY_WAIT_SECRET; - mta_drain(relay); - mta_relay_unref(relay); /* from mta_query_secret() */ -} - -static void -mta_on_smarthost(struct envelope *evp, const char *smarthost) -{ - if (smarthost == NULL) { - log_warnx("warn: Failed to retrieve smarthost " - "for envelope %"PRIx64, evp->id); - m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_evpid(p_queue, evp->id); - m_add_string(p_queue, "Cannot retrieve smarthost"); - m_add_int(p_queue, ESC_OTHER_STATUS); - m_close(p_queue); - return; - } - - log_debug("debug: mta: ... got smarthost for %016"PRIx64": %s", - evp->id, smarthost); - mta_handle_envelope(evp, smarthost); - free(evp); -} - -static void -mta_on_preference(struct mta_relay *relay, int preference) -{ - log_debug("debug: mta: ... got preference for %s: %d", - mta_relay_to_text(relay), preference); - - relay->backuppref = preference; - - relay->status &= ~RELAY_WAIT_PREFERENCE; - mta_drain(relay); - mta_relay_unref(relay); /* from mta_query_preference() */ -} - -static void -mta_on_source(struct mta_relay *relay, struct mta_source *source) -{ - struct mta_connector *c; - void *iter; - int delay, errmask; - - log_debug("debug: mta: ... got source for %s: %s", - mta_relay_to_text(relay), source ? mta_source_to_text(source) : "NULL"); - - relay->lastsource = time(NULL); - delay = DELAY_CHECK_SOURCE_SLOW; - - if (source) { - c = mta_connector(relay, source); - if (c->flags & CONNECTOR_NEW) { - c->flags &= ~CONNECTOR_NEW; - delay = DELAY_CHECK_SOURCE; - } - mta_connect(c); - if ((c->flags & CONNECTOR_ERROR) == 0) - relay->sourceloop = 0; - else - delay = DELAY_CHECK_SOURCE_FAST; - mta_source_unref(source); /* from constructor */ - } - else { - log_warnx("warn: Failed to get source address for %s", - mta_relay_to_text(relay)); - } - - if (tree_count(&relay->connectors) == 0) { - relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; - relay->failstr = "Could not retrieve source address"; - } - if (tree_count(&relay->connectors) < relay->sourceloop) { - relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; - relay->failstr = "No valid route to remote MX"; - - errmask = 0; - iter = NULL; - while (tree_iter(&relay->connectors, &iter, NULL, (void **)&c)) - errmask |= c->flags; - - if (errmask & CONNECTOR_ERROR_ROUTE_SMTP) - relay->failstr = "Destination seem to reject all mails"; - else if (errmask & CONNECTOR_ERROR_ROUTE_NET) - relay->failstr = "Network error on destination MXs"; - else if (errmask & CONNECTOR_ERROR_MX) - relay->failstr = "No MX found for destination"; - else if (errmask & CONNECTOR_ERROR_FAMILY) - relay->failstr = "Address family mismatch on destination MXs"; - else if (errmask & CONNECTOR_ERROR_BLOCKED) - relay->failstr = "All routes to destination blocked"; - else - relay->failstr = "No valid route to destination"; - } - - relay->nextsource = relay->lastsource + delay; - relay->status &= ~RELAY_WAIT_SOURCE; - mta_drain(relay); - mta_relay_unref(relay); /* from mta_query_source() */ -} - -static void -mta_connect(struct mta_connector *c) -{ - struct mta_route *route; - struct mta_mx *mx; - struct mta_limits *l = c->relay->limits; - int limits; - time_t nextconn, now; - - /* toggle the block flag */ - if (mta_is_blocked(c->source, c->relay->domain->name)) - c->flags |= CONNECTOR_ERROR_BLOCKED; - else - c->flags &= ~CONNECTOR_ERROR_BLOCKED; - - again: - - log_debug("debug: mta: connecting with %s", mta_connector_to_text(c)); - - /* Do not connect if this connector has an error. */ - if (c->flags & CONNECTOR_ERROR) { - log_debug("debug: mta: connector error"); - return; - } - - if (c->flags & CONNECTOR_WAIT) { - log_debug("debug: mta: cancelling connector timeout"); - runq_cancel(runq_connector, c); - c->flags &= ~CONNECTOR_WAIT; - } - - /* No job. */ - if (c->relay->ntask == 0) { - log_debug("debug: mta: no task for connector"); - return; - } - - /* Do not create more connections than necessary */ - if ((c->relay->nconn_ready >= c->relay->ntask) || - (c->relay->nconn > 2 && c->relay->nconn >= c->relay->ntask / 2)) { - log_debug("debug: mta: enough connections already"); - return; - } - - limits = 0; - nextconn = now = time(NULL); - - if (c->relay->domain->lastconn + l->conndelay_domain > nextconn) { - log_debug("debug: mta: cannot use domain %s before %llus", - c->relay->domain->name, - (unsigned long long) c->relay->domain->lastconn + l->conndelay_domain - now); - nextconn = c->relay->domain->lastconn + l->conndelay_domain; - } - if (c->relay->domain->nconn >= l->maxconn_per_domain) { - log_debug("debug: mta: hit domain limit"); - limits |= CONNECTOR_LIMIT_DOMAIN; - } - - if (c->source->lastconn + l->conndelay_source > nextconn) { - log_debug("debug: mta: cannot use source %s before %llus", - mta_source_to_text(c->source), - (unsigned long long) c->source->lastconn + l->conndelay_source - now); - nextconn = c->source->lastconn + l->conndelay_source; - } - if (c->source->nconn >= l->maxconn_per_source) { - log_debug("debug: mta: hit source limit"); - limits |= CONNECTOR_LIMIT_SOURCE; - } - - if (c->lastconn + l->conndelay_connector > nextconn) { - log_debug("debug: mta: cannot use %s before %llus", - mta_connector_to_text(c), - (unsigned long long) c->lastconn + l->conndelay_connector - now); - nextconn = c->lastconn + l->conndelay_connector; - } - if (c->nconn >= l->maxconn_per_connector) { - log_debug("debug: mta: hit connector limit"); - limits |= CONNECTOR_LIMIT_CONN; - } - - if (c->relay->lastconn + l->conndelay_relay > nextconn) { - log_debug("debug: mta: cannot use %s before %llus", - mta_relay_to_text(c->relay), - (unsigned long long) c->relay->lastconn + l->conndelay_relay - now); - nextconn = c->relay->lastconn + l->conndelay_relay; - } - if (c->relay->nconn >= l->maxconn_per_relay) { - log_debug("debug: mta: hit relay limit"); - limits |= CONNECTOR_LIMIT_RELAY; - } - - /* We can connect now, find a route */ - if (!limits && nextconn <= now) - route = mta_find_route(c, now, &limits, &nextconn, &mx); - else - route = NULL; - - /* No route */ - if (route == NULL) { - - if (c->flags & CONNECTOR_ERROR) { - /* XXX we might want to clear this flag later */ - log_debug("debug: mta-routing: no route available for %s: errors on connector", - mta_connector_to_text(c)); - return; - } - else if (limits) { - log_debug("debug: mta-routing: no route available for %s: limits reached", - mta_connector_to_text(c)); - nextconn = now + DELAY_CHECK_LIMIT; - } - else { - log_debug("debug: mta-routing: no route available for %s: must wait a bit", - mta_connector_to_text(c)); - } - log_debug("debug: mta: retrying to connect on %s in %llus...", - mta_connector_to_text(c), - (unsigned long long) nextconn - time(NULL)); - c->flags |= CONNECTOR_WAIT; - runq_schedule_at(runq_connector, nextconn, c); - return; - } - - log_debug("debug: mta-routing: spawning new connection on %s", - mta_route_to_text(route)); - - c->nconn += 1; - c->lastconn = time(NULL); - - c->relay->nconn += 1; - c->relay->lastconn = c->lastconn; - c->relay->domain->nconn += 1; - c->relay->domain->lastconn = c->lastconn; - route->nconn += 1; - route->lastconn = c->lastconn; - route->src->nconn += 1; - route->src->lastconn = c->lastconn; - route->dst->nconn += 1; - route->dst->lastconn = c->lastconn; - - mta_session(c->relay, route, mx->mxname); /* this never fails synchronously */ - mta_relay_ref(c->relay); - - goto again; -} - -static void -mta_on_timeout(struct runq *runq, void *arg) -{ - struct mta_connector *connector = arg; - struct mta_relay *relay = arg; - struct mta_route *route = arg; - struct hoststat *hs = arg; - - if (runq == runq_relay) { - log_debug("debug: mta: ... timeout for %s", - mta_relay_to_text(relay)); - relay->status &= ~RELAY_WAIT_CONNECTOR; - mta_drain(relay); - mta_relay_unref(relay); /* from mta_drain() */ - } - else if (runq == runq_connector) { - log_debug("debug: mta: ... timeout for %s", - mta_connector_to_text(connector)); - connector->flags &= ~CONNECTOR_WAIT; - mta_connect(connector); - } - else if (runq == runq_route) { - route->flags &= ~ROUTE_RUNQ; - mta_route_enable(route); - mta_route_unref(route); - } - else if (runq == runq_hoststat) { - log_debug("debug: mta: ... timeout for hoststat %s", - hs->name); - mta_hoststat_remove_entry(hs); - free(hs); - } -} - -static void -mta_route_disable(struct mta_route *route, int penalty, int reason) -{ - unsigned long long delay; - - route->penalty += penalty; - route->lastpenalty = time(NULL); - delay = (unsigned long long)DELAY_ROUTE_BASE * route->penalty * route->penalty; - if (delay > DELAY_ROUTE_MAX) - delay = DELAY_ROUTE_MAX; -#if 0 - delay = 60; -#endif - - log_info("smtp-out: Disabling route %s for %llus", - mta_route_to_text(route), delay); - - if (route->flags & ROUTE_DISABLED) - runq_cancel(runq_route, route); - else - mta_route_ref(route); - - route->flags |= reason & ROUTE_DISABLED; - runq_schedule(runq_route, delay, route); -} - -static void -mta_route_enable(struct mta_route *route) -{ - if (route->flags & ROUTE_DISABLED) { - log_info("smtp-out: Enabling route %s", - mta_route_to_text(route)); - route->flags &= ~ROUTE_DISABLED; - route->flags |= ROUTE_NEW; - route->nerror = 0; - } - - if (route->penalty) { -#if DELAY_QUADRATIC - route->penalty -= 1; - route->lastpenalty = time(NULL); -#else - route->penalty = 0; -#endif - } -} - -static void -mta_drain(struct mta_relay *r) -{ - char buf[64]; - - log_debug("debug: mta: draining %s " - "refcount=%d, ntask=%zu, nconnector=%zu, nconn=%zu", - mta_relay_to_text(r), - r->refcount, r->ntask, tree_count(&r->connectors), r->nconn); - - /* - * All done. - */ - if (r->ntask == 0) { - log_debug("debug: mta: all done for %s", mta_relay_to_text(r)); - return; - } - - /* - * If we know that this relay is failing flush the tasks. - */ - if (r->fail) { - mta_flush(r, r->fail, r->failstr); - return; - } - - /* Query secret if needed. */ - if (r->flags & RELAY_AUTH && r->secret == NULL) - mta_query_secret(r); - - /* Query our preference if needed. */ - if (r->backupname && r->backuppref == -1) - mta_query_preference(r); - - /* Query the domain MXs if needed. */ - if (r->domain->lastmxquery == 0) - mta_query_mx(r); - - /* Query the limits if needed. */ - if (r->limits == NULL) - mta_query_limits(r); - - /* Wait until we are ready to proceed. */ - if (r->status & RELAY_WAITMASK) { - buf[0] = '\0'; - if (r->status & RELAY_WAIT_MX) - (void)strlcat(buf, " MX", sizeof buf); - if (r->status & RELAY_WAIT_PREFERENCE) - (void)strlcat(buf, " preference", sizeof buf); - if (r->status & RELAY_WAIT_SECRET) - (void)strlcat(buf, " secret", sizeof buf); - if (r->status & RELAY_WAIT_SOURCE) - (void)strlcat(buf, " source", sizeof buf); - if (r->status & RELAY_WAIT_CONNECTOR) - (void)strlcat(buf, " connector", sizeof buf); - log_debug("debug: mta: %s waiting for%s", - mta_relay_to_text(r), buf); - return; - } - - /* - * We have pending task, and it's maybe time too try a new source. - */ - if (r->nextsource <= time(NULL)) - mta_query_source(r); - else { - log_debug("debug: mta: scheduling relay %s in %llus...", - mta_relay_to_text(r), - (unsigned long long) r->nextsource - time(NULL)); - runq_schedule_at(runq_relay, r->nextsource, r); - r->status |= RELAY_WAIT_CONNECTOR; - mta_relay_ref(r); - } -} - -static void -mta_flush(struct mta_relay *relay, int fail, const char *error) -{ - struct mta_envelope *e; - struct mta_task *task; - const char *domain; - void *iter; - struct mta_connector *c; - size_t n, r; - - log_debug("debug: mta_flush(%s, %d, \"%s\")", - mta_relay_to_text(relay), fail, error); - - if (fail != IMSG_MTA_DELIVERY_TEMPFAIL && fail != IMSG_MTA_DELIVERY_PERMFAIL) - errx(1, "unexpected delivery status %d", fail); - - n = 0; - while ((task = TAILQ_FIRST(&relay->tasks))) { - TAILQ_REMOVE(&relay->tasks, task, entry); - while ((e = TAILQ_FIRST(&task->envelopes))) { - TAILQ_REMOVE(&task->envelopes, e, entry); - - /* - * host was suspended, cache envelope id in hoststat tree - * so that it can be retried when a delivery succeeds for - * that domain. - */ - domain = strchr(e->dest, '@'); - if (fail == IMSG_MTA_DELIVERY_TEMPFAIL && domain) { - r = 0; - iter = NULL; - while (tree_iter(&relay->connectors, &iter, - NULL, (void **)&c)) { - if (c->flags & CONNECTOR_ERROR_ROUTE) - r++; - } - if (tree_count(&relay->connectors) == r) - mta_hoststat_cache(domain+1, e->id); - } - - mta_delivery_log(e, NULL, relay->domain->name, fail, error); - mta_delivery_notify(e); - - n++; - } - free(task->sender); - free(task); - } - - stat_decrement("mta.task", relay->ntask); - stat_decrement("mta.envelope", n); - relay->ntask = 0; - - /* release all waiting envelopes for the relay */ - if (relay->state & RELAY_HOLDQ) { - m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); - m_add_id(p_queue, relay->id); - m_add_int(p_queue, -1); - m_close(p_queue); - } -} - -/* - * Find a route to use for this connector - */ -static struct mta_route * -mta_find_route(struct mta_connector *c, time_t now, int *limits, - time_t *nextconn, struct mta_mx **pmx) -{ - struct mta_route *route, *best; - struct mta_limits *l = c->relay->limits; - struct mta_mx *mx; - int level, limit_host, limit_route; - int family_mismatch, seen, suspended_route; - time_t tm; - - log_debug("debug: mta-routing: searching new route for %s...", - mta_connector_to_text(c)); - - tm = 0; - limit_host = 0; - limit_route = 0; - suspended_route = 0; - family_mismatch = 0; - level = -1; - best = NULL; - seen = 0; - - TAILQ_FOREACH(mx, &c->relay->domain->mxs, entry) { - /* - * New preference level - */ - if (mx->preference > level) { -#ifndef IGNORE_MX_PREFERENCE - /* - * Use the current best MX if found. - */ - if (best) - break; - - /* - * No candidate found. There are valid MXs at this - * preference level but they reached their limit, or - * we can't connect yet. - */ - if (limit_host || limit_route || tm) - break; - - /* - * If we are a backup MX, do not relay to MXs with - * a greater preference value. - */ - if (c->relay->backuppref >= 0 && - mx->preference >= c->relay->backuppref) - break; - - /* - * Start looking at MXs on this preference level. - */ -#endif - level = mx->preference; - } - - if (mx->host->flags & HOST_IGNORE) - continue; - - /* Found a possibly valid mx */ - seen++; - - if ((c->source->sa && - c->source->sa->sa_family != mx->host->sa->sa_family) || - (l->family && l->family != mx->host->sa->sa_family)) { - log_debug("debug: mta-routing: skipping host %s: AF mismatch", - mta_host_to_text(mx->host)); - family_mismatch = 1; - continue; - } - - if (mx->host->nconn >= l->maxconn_per_host) { - log_debug("debug: mta-routing: skipping host %s: too many connections", - mta_host_to_text(mx->host)); - limit_host = 1; - continue; - } - - if (mx->host->lastconn + l->conndelay_host > now) { - log_debug("debug: mta-routing: skipping host %s: cannot use before %llus", - mta_host_to_text(mx->host), - (unsigned long long) mx->host->lastconn + l->conndelay_host - now); - if (tm == 0 || mx->host->lastconn + l->conndelay_host < tm) - tm = mx->host->lastconn + l->conndelay_host; - continue; - } - - route = mta_route(c->source, mx->host); - - if (route->flags & ROUTE_DISABLED) { - log_debug("debug: mta-routing: skipping route %s: suspend", - mta_route_to_text(route)); - suspended_route |= route->flags & ROUTE_DISABLED; - mta_route_unref(route); /* from here */ - continue; - } - - if (route->nconn && (route->flags & ROUTE_NEW)) { - log_debug("debug: mta-routing: skipping route %s: not validated yet", - mta_route_to_text(route)); - limit_route = 1; - mta_route_unref(route); /* from here */ - continue; - } - - if (route->nconn >= l->maxconn_per_route) { - log_debug("debug: mta-routing: skipping route %s: too many connections", - mta_route_to_text(route)); - limit_route = 1; - mta_route_unref(route); /* from here */ - continue; - } - - if (route->lastconn + l->conndelay_route > now) { - log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after connect)", - mta_route_to_text(route), - (unsigned long long) route->lastconn + l->conndelay_route - now); - if (tm == 0 || route->lastconn + l->conndelay_route < tm) - tm = route->lastconn + l->conndelay_route; - mta_route_unref(route); /* from here */ - continue; - } - - if (route->lastdisc + l->discdelay_route > now) { - log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after disconnect)", - mta_route_to_text(route), - (unsigned long long) route->lastdisc + l->discdelay_route - now); - if (tm == 0 || route->lastdisc + l->discdelay_route < tm) - tm = route->lastdisc + l->discdelay_route; - mta_route_unref(route); /* from here */ - continue; - } - - /* Use the route with the lowest number of connections. */ - if (best && route->nconn >= best->nconn) { - log_debug("debug: mta-routing: skipping route %s: current one is better", - mta_route_to_text(route)); - mta_route_unref(route); /* from here */ - continue; - } - - if (best) - mta_route_unref(best); /* from here */ - best = route; - *pmx = mx; - log_debug("debug: mta-routing: selecting candidate route %s", - mta_route_to_text(route)); - } - - if (best) - return (best); - - /* Order is important */ - if (seen == 0) { - log_info("smtp-out: No MX found for %s", - mta_connector_to_text(c)); - c->flags |= CONNECTOR_ERROR_MX; - } - else if (limit_route) { - log_debug("debug: mta: hit route limit"); - *limits |= CONNECTOR_LIMIT_ROUTE; - } - else if (limit_host) { - log_debug("debug: mta: hit host limit"); - *limits |= CONNECTOR_LIMIT_HOST; - } - else if (tm) { - if (tm > *nextconn) - *nextconn = tm; - } - else if (family_mismatch) { - log_info("smtp-out: Address family mismatch on %s", - mta_connector_to_text(c)); - c->flags |= CONNECTOR_ERROR_FAMILY; - } - else if (suspended_route) { - log_info("smtp-out: No valid route for %s", - mta_connector_to_text(c)); - if (suspended_route & ROUTE_DISABLED_NET) - c->flags |= CONNECTOR_ERROR_ROUTE_NET; - if (suspended_route & ROUTE_DISABLED_SMTP) - c->flags |= CONNECTOR_ERROR_ROUTE_SMTP; - } - - return (NULL); -} - -static void -mta_log(const struct mta_envelope *evp, const char *prefix, const char *source, - const char *relay, const char *status) -{ - log_info("%016"PRIx64" mta delivery evpid=%016"PRIx64" " - "from=<%s> to=<%s> rcpt=<%s> source=\"%s\" " - "relay=\"%s\" delay=%s result=\"%s\" stat=\"%s\"", - evp->session, - evp->id, - evp->task->sender, - evp->dest, - evp->rcpt ? evp->rcpt : "-", - source ? source : "-", - relay, - duration_to_text(time(NULL) - evp->creation), - prefix, - status); -} - -static struct mta_relay * -mta_relay(struct envelope *e, struct relayhost *relayh) -{ - struct dispatcher *dispatcher; - struct mta_relay key, *r; - - dispatcher = dict_xget(env->sc_dispatchers, e->dispatcher); - - memset(&key, 0, sizeof key); - - key.pki_name = dispatcher->u.remote.pki; - key.ca_name = dispatcher->u.remote.ca; - key.authtable = dispatcher->u.remote.auth; - key.sourcetable = dispatcher->u.remote.source; - key.helotable = dispatcher->u.remote.helo_source; - key.heloname = dispatcher->u.remote.helo; - key.srs = dispatcher->u.remote.srs; - - if (relayh->hostname[0]) { - key.domain = mta_domain(relayh->hostname, 1); - } - else { - key.domain = mta_domain(e->dest.domain, 0); - if (dispatcher->u.remote.backup) { - key.backupname = dispatcher->u.remote.backupmx; - if (key.backupname == NULL) - key.backupname = e->smtpname; - } - } - - key.tls = relayh->tls; - key.flags |= relayh->flags; - key.port = relayh->port; - key.authlabel = relayh->authlabel; - if (!key.authlabel[0]) - key.authlabel = NULL; - - if ((key.tls == RELAY_TLS_STARTTLS || key.tls == RELAY_TLS_SMTPS) && - dispatcher->u.remote.tls_noverify == 0) - key.flags |= RELAY_TLS_VERIFY; - - if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)) == NULL) { - r = xcalloc(1, sizeof *r); - TAILQ_INIT(&r->tasks); - r->id = generate_uid(); - r->dispatcher = dispatcher; - r->tls = key.tls; - r->flags = key.flags; - r->domain = key.domain; - r->backupname = key.backupname ? - xstrdup(key.backupname) : NULL; - r->backuppref = -1; - r->port = key.port; - r->pki_name = key.pki_name ? xstrdup(key.pki_name) : NULL; - r->ca_name = key.ca_name ? xstrdup(key.ca_name) : NULL; - if (key.authtable) - r->authtable = xstrdup(key.authtable); - if (key.authlabel) - r->authlabel = xstrdup(key.authlabel); - if (key.sourcetable) - r->sourcetable = xstrdup(key.sourcetable); - if (key.helotable) - r->helotable = xstrdup(key.helotable); - if (key.heloname) - r->heloname = xstrdup(key.heloname); - r->srs = key.srs; - SPLAY_INSERT(mta_relay_tree, &relays, r); - stat_increment("mta.relay", 1); - } else { - mta_domain_unref(key.domain); /* from here */ - } - - r->refcount++; - return (r); -} - -static void -mta_relay_ref(struct mta_relay *r) -{ - r->refcount++; -} - -static void -mta_relay_unref(struct mta_relay *relay) -{ - struct mta_connector *c; - - if (--relay->refcount) - return; - - /* Make sure they are no envelopes held for this relay */ - if (relay->state & RELAY_HOLDQ) { - m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); - m_add_id(p_queue, relay->id); - m_add_int(p_queue, 0); - m_close(p_queue); - } - - log_debug("debug: mta: freeing %s", mta_relay_to_text(relay)); - SPLAY_REMOVE(mta_relay_tree, &relays, relay); - - while ((tree_poproot(&relay->connectors, NULL, (void**)&c))) - mta_connector_free(c); - - free(relay->authlabel); - free(relay->authtable); - free(relay->backupname); - free(relay->pki_name); - free(relay->ca_name); - free(relay->helotable); - free(relay->heloname); - free(relay->secret); - free(relay->sourcetable); - - mta_domain_unref(relay->domain); /* from constructor */ - free(relay); - stat_decrement("mta.relay", 1); -} - -const char * -mta_relay_to_text(struct mta_relay *relay) -{ - static char buf[1024]; - char tmp[32]; - const char *sep = ","; - - (void)snprintf(buf, sizeof buf, "[relay:%s", relay->domain->name); - - if (relay->port) { - (void)strlcat(buf, sep, sizeof buf); - (void)snprintf(tmp, sizeof tmp, "port=%d", (int)relay->port); - (void)strlcat(buf, tmp, sizeof buf); - } - - (void)strlcat(buf, sep, sizeof buf); - switch(relay->tls) { - case RELAY_TLS_OPPORTUNISTIC: - (void)strlcat(buf, "smtp", sizeof buf); - break; - case RELAY_TLS_STARTTLS: - (void)strlcat(buf, "smtp+tls", sizeof buf); - break; - case RELAY_TLS_SMTPS: - (void)strlcat(buf, "smtps", sizeof buf); - break; - case RELAY_TLS_NO: - if (relay->flags & RELAY_LMTP) - (void)strlcat(buf, "lmtp", sizeof buf); - else - (void)strlcat(buf, "smtp+notls", sizeof buf); - break; - default: - (void)strlcat(buf, "???", sizeof buf); - } - - if (relay->flags & RELAY_AUTH) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "auth=", sizeof buf); - (void)strlcat(buf, relay->authtable, sizeof buf); - (void)strlcat(buf, ":", sizeof buf); - (void)strlcat(buf, relay->authlabel, sizeof buf); - } - - if (relay->pki_name) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "pki_name=", sizeof buf); - (void)strlcat(buf, relay->pki_name, sizeof buf); - } - - if (relay->domain->as_host) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "mx", sizeof buf); - } - - if (relay->backupname) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "backup=", sizeof buf); - (void)strlcat(buf, relay->backupname, sizeof buf); - } - - if (relay->sourcetable) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "sourcetable=", sizeof buf); - (void)strlcat(buf, relay->sourcetable, sizeof buf); - } - - if (relay->helotable) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "helotable=", sizeof buf); - (void)strlcat(buf, relay->helotable, sizeof buf); - } - - if (relay->heloname) { - (void)strlcat(buf, sep, sizeof buf); - (void)strlcat(buf, "heloname=", sizeof buf); - (void)strlcat(buf, relay->heloname, sizeof buf); - } - - (void)strlcat(buf, "]", sizeof buf); - - return (buf); -} - -static void -mta_relay_show(struct mta_relay *r, struct mproc *p, uint32_t id, time_t t) -{ - struct mta_connector *c; - void *iter; - char buf[1024], flags[1024], dur[64]; - time_t to; - - flags[0] = '\0'; - -#define SHOWSTATUS(f, n) do { \ - if (r->status & (f)) { \ - if (flags[0]) \ - (void)strlcat(flags, ",", sizeof(flags)); \ - (void)strlcat(flags, (n), sizeof(flags)); \ - } \ - } while(0) - - SHOWSTATUS(RELAY_WAIT_MX, "MX"); - SHOWSTATUS(RELAY_WAIT_PREFERENCE, "preference"); - SHOWSTATUS(RELAY_WAIT_SECRET, "secret"); - SHOWSTATUS(RELAY_WAIT_LIMITS, "limits"); - SHOWSTATUS(RELAY_WAIT_SOURCE, "source"); - SHOWSTATUS(RELAY_WAIT_CONNECTOR, "connector"); -#undef SHOWSTATUS - - if (runq_pending(runq_relay, r, &to)) - (void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t)); - else - (void)strlcpy(dur, "-", sizeof(dur)); - - (void)snprintf(buf, sizeof(buf), "%s refcount=%d ntask=%zu nconn=%zu lastconn=%s timeout=%s wait=%s%s", - mta_relay_to_text(r), - r->refcount, - r->ntask, - r->nconn, - r->lastconn ? duration_to_text(t - r->lastconn) : "-", - dur, - flags, - (r->state & RELAY_ONHOLD) ? "ONHOLD" : ""); - m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf, strlen(buf) + 1); - - iter = NULL; - while (tree_iter(&r->connectors, &iter, NULL, (void **)&c)) { - - if (runq_pending(runq_connector, c, &to)) - (void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t)); - else - (void)strlcpy(dur, "-", sizeof(dur)); - - flags[0] = '\0'; - -#define SHOWFLAG(f, n) do { \ - if (c->flags & (f)) { \ - if (flags[0]) \ - (void)strlcat(flags, ",", sizeof(flags)); \ - (void)strlcat(flags, (n), sizeof(flags)); \ - } \ - } while(0) - - SHOWFLAG(CONNECTOR_NEW, "NEW"); - SHOWFLAG(CONNECTOR_WAIT, "WAIT"); - - SHOWFLAG(CONNECTOR_ERROR_FAMILY, "ERROR_FAMILY"); - SHOWFLAG(CONNECTOR_ERROR_SOURCE, "ERROR_SOURCE"); - SHOWFLAG(CONNECTOR_ERROR_MX, "ERROR_MX"); - SHOWFLAG(CONNECTOR_ERROR_ROUTE_NET, "ERROR_ROUTE_NET"); - SHOWFLAG(CONNECTOR_ERROR_ROUTE_SMTP, "ERROR_ROUTE_SMTP"); - SHOWFLAG(CONNECTOR_ERROR_BLOCKED, "ERROR_BLOCKED"); - - SHOWFLAG(CONNECTOR_LIMIT_HOST, "LIMIT_HOST"); - SHOWFLAG(CONNECTOR_LIMIT_ROUTE, "LIMIT_ROUTE"); - SHOWFLAG(CONNECTOR_LIMIT_SOURCE, "LIMIT_SOURCE"); - SHOWFLAG(CONNECTOR_LIMIT_RELAY, "LIMIT_RELAY"); - SHOWFLAG(CONNECTOR_LIMIT_CONN, "LIMIT_CONN"); - SHOWFLAG(CONNECTOR_LIMIT_DOMAIN, "LIMIT_DOMAIN"); -#undef SHOWFLAG - - (void)snprintf(buf, sizeof(buf), - " connector %s refcount=%d nconn=%zu lastconn=%s timeout=%s flags=%s", - mta_source_to_text(c->source), - c->refcount, - c->nconn, - c->lastconn ? duration_to_text(t - c->lastconn) : "-", - dur, - flags); - m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf, - strlen(buf) + 1); - - - } -} - -static int -mta_relay_cmp(const struct mta_relay *a, const struct mta_relay *b) -{ - int r; - - if (a->domain < b->domain) - return (-1); - if (a->domain > b->domain) - return (1); - - if (a->tls < b->tls) - return (-1); - if (a->tls > b->tls) - return (1); - - if (a->flags < b->flags) - return (-1); - if (a->flags > b->flags) - return (1); - - if (a->port < b->port) - return (-1); - if (a->port > b->port) - return (1); - - if (a->authtable == NULL && b->authtable) - return (-1); - if (a->authtable && b->authtable == NULL) - return (1); - if (a->authtable && ((r = strcmp(a->authtable, b->authtable)))) - return (r); - if (a->authlabel == NULL && b->authlabel) - return (-1); - if (a->authlabel && b->authlabel == NULL) - return (1); - if (a->authlabel && ((r = strcmp(a->authlabel, b->authlabel)))) - return (r); - if (a->sourcetable == NULL && b->sourcetable) - return (-1); - if (a->sourcetable && b->sourcetable == NULL) - return (1); - if (a->sourcetable && ((r = strcmp(a->sourcetable, b->sourcetable)))) - return (r); - if (a->helotable == NULL && b->helotable) - return (-1); - if (a->helotable && b->helotable == NULL) - return (1); - if (a->helotable && ((r = strcmp(a->helotable, b->helotable)))) - return (r); - if (a->heloname == NULL && b->heloname) - return (-1); - if (a->heloname && b->heloname == NULL) - return (1); - if (a->heloname && ((r = strcmp(a->heloname, b->heloname)))) - return (r); - - if (a->pki_name == NULL && b->pki_name) - return (-1); - if (a->pki_name && b->pki_name == NULL) - return (1); - if (a->pki_name && ((r = strcmp(a->pki_name, b->pki_name)))) - return (r); - - if (a->ca_name == NULL && b->ca_name) - return (-1); - if (a->ca_name && b->ca_name == NULL) - return (1); - if (a->ca_name && ((r = strcmp(a->ca_name, b->ca_name)))) - return (r); - - if (a->backupname == NULL && b->backupname) - return (-1); - if (a->backupname && b->backupname == NULL) - return (1); - if (a->backupname && ((r = strcmp(a->backupname, b->backupname)))) - return (r); - - if (a->srs < b->srs) - return (-1); - if (a->srs > b->srs) - return (1); - - return (0); -} - -SPLAY_GENERATE(mta_relay_tree, mta_relay, entry, mta_relay_cmp); - -static struct mta_host * -mta_host(const struct sockaddr *sa) -{ - struct mta_host key, *h; - struct sockaddr_storage ss; - - memmove(&ss, sa, SA_LEN(sa)); - key.sa = (struct sockaddr*)&ss; - h = SPLAY_FIND(mta_host_tree, &hosts, &key); - - if (h == NULL) { - h = xcalloc(1, sizeof(*h)); - h->sa = xmemdup(sa, SA_LEN(sa)); - SPLAY_INSERT(mta_host_tree, &hosts, h); - stat_increment("mta.host", 1); - } - - h->refcount++; - return (h); -} - -static void -mta_host_ref(struct mta_host *h) -{ - h->refcount++; -} - -static void -mta_host_unref(struct mta_host *h) -{ - if (--h->refcount) - return; - - SPLAY_REMOVE(mta_host_tree, &hosts, h); - free(h->sa); - free(h->ptrname); - free(h); - stat_decrement("mta.host", 1); -} - -const char * -mta_host_to_text(struct mta_host *h) -{ - static char buf[1024]; - - if (h->ptrname) - (void)snprintf(buf, sizeof buf, "%s (%s)", - sa_to_text(h->sa), h->ptrname); - else - (void)snprintf(buf, sizeof buf, "%s", sa_to_text(h->sa)); - - return (buf); -} - -static int -mta_host_cmp(const struct mta_host *a, const struct mta_host *b) -{ - if (SA_LEN(a->sa) < SA_LEN(b->sa)) - return (-1); - if (SA_LEN(a->sa) > SA_LEN(b->sa)) - return (1); - return (memcmp(a->sa, b->sa, SA_LEN(a->sa))); -} - -SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp); - -static struct mta_domain * -mta_domain(char *name, int as_host) -{ - struct mta_domain key, *d; - - key.name = name; - key.as_host = as_host; - d = SPLAY_FIND(mta_domain_tree, &domains, &key); - - if (d == NULL) { - d = xcalloc(1, sizeof(*d)); - d->name = xstrdup(name); - d->as_host = as_host; - TAILQ_INIT(&d->mxs); - SPLAY_INSERT(mta_domain_tree, &domains, d); - stat_increment("mta.domain", 1); - } - - d->refcount++; - return (d); -} - -#if 0 -static void -mta_domain_ref(struct mta_domain *d) -{ - d->refcount++; -} -#endif - -static void -mta_domain_unref(struct mta_domain *d) -{ - struct mta_mx *mx; - - if (--d->refcount) - return; - - while ((mx = TAILQ_FIRST(&d->mxs))) { - TAILQ_REMOVE(&d->mxs, mx, entry); - mta_host_unref(mx->host); /* from IMSG_DNS_HOST */ - free(mx->mxname); - free(mx); - } - - SPLAY_REMOVE(mta_domain_tree, &domains, d); - free(d->name); - free(d); - stat_decrement("mta.domain", 1); -} - -static int -mta_domain_cmp(const struct mta_domain *a, const struct mta_domain *b) -{ - if (a->as_host < b->as_host) - return (-1); - if (a->as_host > b->as_host) - return (1); - return (strcasecmp(a->name, b->name)); -} - -SPLAY_GENERATE(mta_domain_tree, mta_domain, entry, mta_domain_cmp); - -static struct mta_source * -mta_source(const struct sockaddr *sa) -{ - struct mta_source key, *s; - struct sockaddr_storage ss; - - if (sa) { - memmove(&ss, sa, SA_LEN(sa)); - key.sa = (struct sockaddr*)&ss; - } else - key.sa = NULL; - s = SPLAY_FIND(mta_source_tree, &sources, &key); - - if (s == NULL) { - s = xcalloc(1, sizeof(*s)); - if (sa) - s->sa = xmemdup(sa, SA_LEN(sa)); - SPLAY_INSERT(mta_source_tree, &sources, s); - stat_increment("mta.source", 1); - } - - s->refcount++; - return (s); -} - -static void -mta_source_ref(struct mta_source *s) -{ - s->refcount++; -} - -static void -mta_source_unref(struct mta_source *s) -{ - if (--s->refcount) - return; - - SPLAY_REMOVE(mta_source_tree, &sources, s); - free(s->sa); - free(s); - stat_decrement("mta.source", 1); -} - -static const char * -mta_source_to_text(struct mta_source *s) -{ - static char buf[1024]; - - if (s->sa == NULL) - return "[]"; - (void)snprintf(buf, sizeof buf, "%s", sa_to_text(s->sa)); - return (buf); -} - -static int -mta_source_cmp(const struct mta_source *a, const struct mta_source *b) -{ - if (a->sa == NULL) - return ((b->sa == NULL) ? 0 : -1); - if (b->sa == NULL) - return (1); - if (SA_LEN(a->sa) < SA_LEN(b->sa)) - return (-1); - if (SA_LEN(a->sa) > SA_LEN(b->sa)) - return (1); - return (memcmp(a->sa, b->sa, SA_LEN(a->sa))); -} - -SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp); - -static struct mta_connector * -mta_connector(struct mta_relay *relay, struct mta_source *source) -{ - struct mta_connector *c; - - c = tree_get(&relay->connectors, (uintptr_t)(source)); - if (c == NULL) { - c = xcalloc(1, sizeof(*c)); - c->relay = relay; - c->source = source; - c->flags |= CONNECTOR_NEW; - mta_source_ref(source); - tree_xset(&relay->connectors, (uintptr_t)(source), c); - stat_increment("mta.connector", 1); - log_debug("debug: mta: new %s", mta_connector_to_text(c)); - } - - return (c); -} - -static void -mta_connector_free(struct mta_connector *c) -{ - log_debug("debug: mta: freeing %s", - mta_connector_to_text(c)); - - if (c->flags & CONNECTOR_WAIT) { - log_debug("debug: mta: cancelling timeout for %s", - mta_connector_to_text(c)); - runq_cancel(runq_connector, c); - } - mta_source_unref(c->source); /* from constructor */ - free(c); - - stat_decrement("mta.connector", 1); -} - -static const char * -mta_connector_to_text(struct mta_connector *c) -{ - static char buf[1024]; - - (void)snprintf(buf, sizeof buf, "[connector:%s->%s,0x%x]", - mta_source_to_text(c->source), - mta_relay_to_text(c->relay), - c->flags); - return (buf); -} - -static struct mta_route * -mta_route(struct mta_source *src, struct mta_host *dst) -{ - struct mta_route key, *r; - static uint64_t rid = 0; - - key.src = src; - key.dst = dst; - r = SPLAY_FIND(mta_route_tree, &routes, &key); - - if (r == NULL) { - r = xcalloc(1, sizeof(*r)); - r->src = src; - r->dst = dst; - r->flags |= ROUTE_NEW; - r->id = ++rid; - SPLAY_INSERT(mta_route_tree, &routes, r); - mta_source_ref(src); - mta_host_ref(dst); - stat_increment("mta.route", 1); - } - else if (r->flags & ROUTE_RUNQ) { - log_debug("debug: mta: mta_route_ref(): cancelling runq for route %s", - mta_route_to_text(r)); - r->flags &= ~(ROUTE_RUNQ | ROUTE_KEEPALIVE); - runq_cancel(runq_route, r); - r->refcount--; /* from mta_route_unref() */ - } - - r->refcount++; - return (r); -} - -static void -mta_route_ref(struct mta_route *r) -{ - r->refcount++; -} - -static void -mta_route_unref(struct mta_route *r) -{ - time_t sched, now; - int delay; - - if (--r->refcount) - return; - - /* - * Nothing references this route, but we might want to keep it alive - * for a while. - */ - now = time(NULL); - sched = 0; - - if (r->penalty) { -#if DELAY_QUADRATIC - delay = DELAY_ROUTE_BASE * r->penalty * r->penalty; -#else - delay = 15 * 60; -#endif - if (delay > DELAY_ROUTE_MAX) - delay = DELAY_ROUTE_MAX; - sched = r->lastpenalty + delay; - log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (penalty %d)", - mta_route_to_text(r), (unsigned long long) sched - now, r->penalty); - } else if (!(r->flags & ROUTE_KEEPALIVE)) { - if (r->lastconn + max_seen_conndelay_route > now) - sched = r->lastconn + max_seen_conndelay_route; - if (r->lastdisc + max_seen_discdelay_route > now && - r->lastdisc + max_seen_discdelay_route < sched) - sched = r->lastdisc + max_seen_discdelay_route; - - if (sched > now) - log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (imposed delay)", - mta_route_to_text(r), (unsigned long long) sched - now); - } - - if (sched > now) { - r->flags |= ROUTE_RUNQ; - runq_schedule_at(runq_route, sched, r); - r->refcount++; - return; - } - - log_debug("debug: mta: mta_route_unref(): really discarding route %s", - mta_route_to_text(r)); - - SPLAY_REMOVE(mta_route_tree, &routes, r); - mta_source_unref(r->src); /* from constructor */ - mta_host_unref(r->dst); /* from constructor */ - free(r); - stat_decrement("mta.route", 1); -} - -static const char * -mta_route_to_text(struct mta_route *r) -{ - static char buf[1024]; - - (void)snprintf(buf, sizeof buf, "%s <-> %s", - mta_source_to_text(r->src), - mta_host_to_text(r->dst)); - - return (buf); -} - -static int -mta_route_cmp(const struct mta_route *a, const struct mta_route *b) -{ - if (a->src < b->src) - return (-1); - if (a->src > b->src) - return (1); - - if (a->dst < b->dst) - return (-1); - if (a->dst > b->dst) - return (1); - - return (0); -} - -SPLAY_GENERATE(mta_route_tree, mta_route, entry, mta_route_cmp); - -void -mta_block(struct mta_source *src, char *dom) -{ - struct mta_block key, *b; - - key.source = src; - key.domain = dom; - - b = SPLAY_FIND(mta_block_tree, &blocks, &key); - if (b != NULL) - return; - - b = xcalloc(1, sizeof(*b)); - if (dom) - b->domain = xstrdup(dom); - b->source = src; - mta_source_ref(src); - SPLAY_INSERT(mta_block_tree, &blocks, b); -} - -void -mta_unblock(struct mta_source *src, char *dom) -{ - struct mta_block key, *b; - - key.source = src; - key.domain = dom; - - b = SPLAY_FIND(mta_block_tree, &blocks, &key); - if (b == NULL) - return; - - SPLAY_REMOVE(mta_block_tree, &blocks, b); - - mta_source_unref(b->source); - free(b->domain); - free(b); -} - -int -mta_is_blocked(struct mta_source *src, char *dom) -{ - struct mta_block key; - - key.source = src; - key.domain = dom; - - if (SPLAY_FIND(mta_block_tree, &blocks, &key)) - return (1); - - return (0); -} - -static -int -mta_block_cmp(const struct mta_block *a, const struct mta_block *b) -{ - if (a->source < b->source) - return (-1); - if (a->source > b->source) - return (1); - if (!a->domain && b->domain) - return (-1); - if (a->domain && !b->domain) - return (1); - if (a->domain == b->domain) - return (0); - return (strcasecmp(a->domain, b->domain)); -} - -SPLAY_GENERATE(mta_block_tree, mta_block, entry, mta_block_cmp); - - - -/* hoststat errors are not critical, we do best effort */ -void -mta_hoststat_update(const char *host, const char *error) -{ - struct hoststat *hs = NULL; - char buf[HOST_NAME_MAX+1]; - - if (!lowercase(buf, host, sizeof buf)) - return; - - hs = dict_get(&hoststat, buf); - if (hs == NULL) { - if ((hs = calloc(1, sizeof *hs)) == NULL) - return; - tree_init(&hs->deferred); - runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY, hs); - } - (void)strlcpy(hs->name, buf, sizeof hs->name); - (void)strlcpy(hs->error, error, sizeof hs->error); - hs->tm = time(NULL); - dict_set(&hoststat, buf, hs); - - runq_cancel(runq_hoststat, hs); - runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY, hs); -} - -void -mta_hoststat_cache(const char *host, uint64_t evpid) -{ - struct hoststat *hs = NULL; - char buf[HOST_NAME_MAX+1]; - - if (!lowercase(buf, host, sizeof buf)) - return; - - hs = dict_get(&hoststat, buf); - if (hs == NULL) - return; - - if (tree_count(&hs->deferred) >= env->sc_mta_max_deferred) - return; - - tree_set(&hs->deferred, evpid, NULL); -} - -void -mta_hoststat_uncache(const char *host, uint64_t evpid) -{ - struct hoststat *hs = NULL; - char buf[HOST_NAME_MAX+1]; - - if (!lowercase(buf, host, sizeof buf)) - return; - - hs = dict_get(&hoststat, buf); - if (hs == NULL) - return; - - tree_pop(&hs->deferred, evpid); -} - -void -mta_hoststat_reschedule(const char *host) -{ - struct hoststat *hs = NULL; - char buf[HOST_NAME_MAX+1]; - uint64_t evpid; - - if (!lowercase(buf, host, sizeof buf)) - return; - - hs = dict_get(&hoststat, buf); - if (hs == NULL) - return; - - while (tree_poproot(&hs->deferred, &evpid, NULL)) { - m_compose(p_queue, IMSG_MTA_SCHEDULE, 0, 0, -1, - &evpid, sizeof evpid); - } -} - -static void -mta_hoststat_remove_entry(struct hoststat *hs) -{ - while (tree_poproot(&hs->deferred, NULL, NULL)) - ; - dict_pop(&hoststat, hs->name); - runq_cancel(runq_hoststat, hs); -} diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c deleted file mode 100644 index ad1c3c84..00000000 --- a/smtpd/mta_session.c +++ /dev/null @@ -1,2008 +0,0 @@ -/* $OpenBSD: mta_session.c,v 1.136 2020/05/21 15:38:05 millert Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/uio.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <netdb.h> -#include <openssl/ssl.h> -#include <pwd.h> -#include <resolv.h> -#include <limits.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -#define MAX_TRYBEFOREDISABLE 10 - -#define MTA_HIWAT 65535 - -enum mta_state { - MTA_INIT, - MTA_BANNER, - MTA_EHLO, - MTA_HELO, - MTA_LHLO, - MTA_STARTTLS, - MTA_AUTH, - MTA_AUTH_PLAIN, - MTA_AUTH_LOGIN, - MTA_AUTH_LOGIN_USER, - MTA_AUTH_LOGIN_PASS, - MTA_READY, - MTA_MAIL, - MTA_RCPT, - MTA_DATA, - MTA_BODY, - MTA_EOM, - MTA_LMTP_EOM, - MTA_RSET, - MTA_QUIT, -}; - -#define MTA_FORCE_ANYSSL 0x0001 -#define MTA_FORCE_SMTPS 0x0002 -#define MTA_FORCE_TLS 0x0004 -#define MTA_FORCE_PLAIN 0x0008 -#define MTA_WANT_SECURE 0x0010 -#define MTA_DOWNGRADE_PLAIN 0x0080 - -#define MTA_TLS 0x0100 -#define MTA_TLS_VERIFIED 0x0200 - -#define MTA_FREE 0x0400 -#define MTA_LMTP 0x0800 -#define MTA_WAIT 0x1000 -#define MTA_HANGON 0x2000 -#define MTA_RECONN 0x4000 - -#define MTA_EXT_STARTTLS 0x01 -#define MTA_EXT_PIPELINING 0x02 -#define MTA_EXT_AUTH 0x04 -#define MTA_EXT_AUTH_PLAIN 0x08 -#define MTA_EXT_AUTH_LOGIN 0x10 -#define MTA_EXT_SIZE 0x20 - -struct mta_session { - uint64_t id; - struct mta_relay *relay; - struct mta_route *route; - char *helo; - char *mxname; - - char *username; - - int flags; - - int attempt; - int use_smtps; - int use_starttls; - int use_smtp_tls; - int ready; - - struct event ev; - struct io *io; - int ext; - - size_t ext_size; - - size_t msgtried; - size_t msgcount; - size_t rcptcount; - int hangon; - - enum mta_state state; - struct mta_task *task; - struct mta_envelope *currevp; - FILE *datafp; - size_t datalen; - - size_t failures; - - char replybuf[2048]; -}; - -static void mta_session_init(void); -static void mta_start(int fd, short ev, void *arg); -static void mta_io(struct io *, int, void *); -static void mta_free(struct mta_session *); -static void mta_getnameinfo_cb(void *, int, const char *, const char *); -static void mta_on_ptr(void *, void *, void *); -static void mta_on_timeout(struct runq *, void *); -static void mta_connect(struct mta_session *); -static void mta_enter_state(struct mta_session *, int); -static void mta_flush_task(struct mta_session *, int, const char *, size_t, int); -static void mta_error(struct mta_session *, const char *, ...); -static void mta_send(struct mta_session *, char *, ...); -static ssize_t mta_queue_data(struct mta_session *); -static void mta_response(struct mta_session *, char *); -static const char * mta_strstate(int); -static void mta_cert_init(struct mta_session *); -static void mta_cert_init_cb(void *, int, const char *, const void *, size_t); -static void mta_cert_verify(struct mta_session *); -static void mta_cert_verify_cb(void *, int); -static void mta_tls_verified(struct mta_session *); -static struct mta_session *mta_tree_pop(struct tree *, uint64_t); -static const char * dsn_strret(enum dsn_ret); -static const char * dsn_strnotify(uint8_t); - -void mta_hoststat_update(const char *, const char *); -void mta_hoststat_reschedule(const char *); -void mta_hoststat_cache(const char *, uint64_t); -void mta_hoststat_uncache(const char *, uint64_t); - - -static void mta_filter_begin(struct mta_session *); -static void mta_filter_end(struct mta_session *); -static void mta_connected(struct mta_session *); -static void mta_disconnected(struct mta_session *); - -static void mta_report_link_connect(struct mta_session *, const char *, int, - const struct sockaddr_storage *, - const struct sockaddr_storage *); -static void mta_report_link_greeting(struct mta_session *, const char *); -static void mta_report_link_identify(struct mta_session *, const char *, const char *); -static void mta_report_link_tls(struct mta_session *, const char *); -static void mta_report_link_disconnect(struct mta_session *); -static void mta_report_link_auth(struct mta_session *, const char *, const char *); -static void mta_report_tx_reset(struct mta_session *, uint32_t); -static void mta_report_tx_begin(struct mta_session *, uint32_t); -static void mta_report_tx_mail(struct mta_session *, uint32_t, const char *, int); -static void mta_report_tx_rcpt(struct mta_session *, uint32_t, const char *, int); -static void mta_report_tx_envelope(struct mta_session *, uint32_t, uint64_t); -static void mta_report_tx_data(struct mta_session *, uint32_t, int); -static void mta_report_tx_commit(struct mta_session *, uint32_t, size_t); -static void mta_report_tx_rollback(struct mta_session *, uint32_t); -static void mta_report_protocol_client(struct mta_session *, const char *); -static void mta_report_protocol_server(struct mta_session *, const char *); -#if 0 -static void mta_report_filter_response(struct mta_session *, int, int, const char *); -#endif -static void mta_report_timeout(struct mta_session *); - - -static struct tree wait_helo; -static struct tree wait_ptr; -static struct tree wait_fd; -static struct tree wait_tls_init; -static struct tree wait_tls_verify; - -static struct runq *hangon; - -#define SESSION_FILTERED(s) \ - ((s)->relay->dispatcher->u.remote.filtername) - -static void -mta_session_init(void) -{ - static int init = 0; - - if (!init) { - tree_init(&wait_helo); - tree_init(&wait_ptr); - tree_init(&wait_fd); - tree_init(&wait_tls_init); - tree_init(&wait_tls_verify); - runq_init(&hangon, mta_on_timeout); - init = 1; - } -} - -void -mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname) -{ - struct mta_session *s; - struct timeval tv; - - mta_session_init(); - - s = xcalloc(1, sizeof *s); - s->id = generate_uid(); - s->relay = relay; - s->route = route; - s->mxname = xstrdup(mxname); - - mta_filter_begin(s); - - if (relay->flags & RELAY_LMTP) - s->flags |= MTA_LMTP; - switch (relay->tls) { - case RELAY_TLS_SMTPS: - s->flags |= MTA_FORCE_SMTPS; - s->flags |= MTA_WANT_SECURE; - break; - case RELAY_TLS_STARTTLS: - s->flags |= MTA_FORCE_TLS; - s->flags |= MTA_WANT_SECURE; - break; - case RELAY_TLS_OPPORTUNISTIC: - /* do not force anything, try tls then smtp */ - break; - case RELAY_TLS_NO: - s->flags |= MTA_FORCE_PLAIN; - break; - default: - fatalx("bad value for relay->tls: %d", relay->tls); - } - - log_debug("debug: mta: %p: spawned for relay %s", s, - mta_relay_to_text(relay)); - stat_increment("mta.session", 1); - - if (route->dst->ptrname || route->dst->lastptrquery) { - /* We want to delay the connection since to always notify - * the relay asynchronously. - */ - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_set(&s->ev, mta_start, s); - evtimer_add(&s->ev, &tv); - } else if (waitq_wait(&route->dst->ptrname, mta_on_ptr, s)) { - resolver_getnameinfo(s->route->dst->sa, 0, mta_getnameinfo_cb, s); - } -} - -void -mta_session_imsg(struct mproc *p, struct imsg *imsg) -{ - struct mta_session *s; - struct msg m; - uint64_t reqid; - const char *name; - int status; - struct stat sb; - - switch (imsg->hdr.type) { - - case IMSG_MTA_OPEN_MESSAGE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - s = mta_tree_pop(&wait_fd, reqid); - if (s == NULL) { - if (imsg->fd != -1) - close(imsg->fd); - return; - } - - if (imsg->fd == -1) { - log_debug("debug: mta: failed to obtain msg fd"); - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Could not get message fd", 0, 0); - mta_enter_state(s, MTA_READY); - return; - } - - if ((s->ext & MTA_EXT_SIZE) && s->ext_size != 0) { - if (fstat(imsg->fd, &sb) == -1) { - log_debug("debug: mta: failed to stat msg fd"); - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Could not stat message fd", 0, 0); - mta_enter_state(s, MTA_READY); - close(imsg->fd); - return; - } - if (sb.st_size > (off_t)s->ext_size) { - log_debug("debug: mta: message too large for peer"); - mta_flush_task(s, IMSG_MTA_DELIVERY_PERMFAIL, - "message too large for peer", 0, 0); - mta_enter_state(s, MTA_READY); - close(imsg->fd); - return; - } - } - - s->datafp = fdopen(imsg->fd, "r"); - if (s->datafp == NULL) - fatal("mta: fdopen"); - - mta_enter_state(s, MTA_MAIL); - return; - - case IMSG_MTA_LOOKUP_HELO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &status); - if (status == LKA_OK) - m_get_string(&m, &name); - m_end(&m); - - s = mta_tree_pop(&wait_helo, reqid); - if (s == NULL) - return; - - if (status == LKA_OK) { - s->helo = xstrdup(name); - mta_connect(s); - } else { - mta_source_error(s->relay, s->route, - "Failed to retrieve helo string"); - mta_free(s); - } - return; - - default: - errx(1, "mta_session_imsg: unexpected %s imsg", - imsg_to_str(imsg->hdr.type)); - } -} - -static struct mta_session * -mta_tree_pop(struct tree *wait, uint64_t reqid) -{ - struct mta_session *s; - - s = tree_xpop(wait, reqid); - if (s->flags & MTA_FREE) { - log_debug("debug: mta: %p: zombie session", s); - mta_free(s); - return (NULL); - } - s->flags &= ~MTA_WAIT; - - return (s); -} - -static void -mta_free(struct mta_session *s) -{ - struct mta_relay *relay; - struct mta_route *route; - - log_debug("debug: mta: %p: session done", s); - - mta_disconnected(s); - - if (s->ready) - s->relay->nconn_ready -= 1; - - if (s->flags & MTA_HANGON) { - log_debug("debug: mta: %p: cancelling hangon timer", s); - runq_cancel(hangon, s); - } - - if (s->io) - io_free(s->io); - - if (s->task) - fatalx("current task should have been deleted already"); - if (s->datafp) { - fclose(s->datafp); - s->datalen = 0; - } - free(s->helo); - - relay = s->relay; - route = s->route; - free(s->username); - free(s->mxname); - free(s); - stat_decrement("mta.session", 1); - mta_route_collect(relay, route); -} - -static void -mta_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv) -{ - struct mta_session *s = arg; - struct mta_host *h; - - h = s->route->dst; - h->lastptrquery = time(NULL); - if (host) - h->ptrname = xstrdup(host); - waitq_run(&h->ptrname, h->ptrname); -} - -static void -mta_on_timeout(struct runq *runq, void *arg) -{ - struct mta_session *s = arg; - - log_debug("mta: timeout for session hangon"); - - s->flags &= ~MTA_HANGON; - s->hangon++; - - mta_enter_state(s, MTA_READY); -} - -static void -mta_on_ptr(void *tag, void *arg, void *data) -{ - struct mta_session *s = arg; - - mta_connect(s); -} - -static void -mta_start(int fd, short ev, void *arg) -{ - struct mta_session *s = arg; - - mta_connect(s); -} - -static void -mta_connect(struct mta_session *s) -{ - struct sockaddr_storage ss; - struct sockaddr *sa; - int portno; - const char *schema; - - if (s->helo == NULL) { - if (s->relay->helotable && s->route->src->sa) { - m_create(p_lka, IMSG_MTA_LOOKUP_HELO, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->relay->helotable); - m_add_sockaddr(p_lka, s->route->src->sa); - m_close(p_lka); - tree_xset(&wait_helo, s->id, s); - s->flags |= MTA_WAIT; - return; - } - else if (s->relay->heloname) - s->helo = xstrdup(s->relay->heloname); - else - s->helo = xstrdup(env->sc_hostname); - } - - if (s->io) { - io_free(s->io); - s->io = NULL; - } - - s->use_smtps = s->use_starttls = s->use_smtp_tls = 0; - - switch (s->attempt) { - case 0: - if (s->flags & MTA_FORCE_SMTPS) - s->use_smtps = 1; /* smtps */ - else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL)) - s->use_starttls = 1; /* tls, tls+smtps */ - else if (!(s->flags & MTA_FORCE_PLAIN)) - s->use_smtp_tls = 1; - break; - case 1: - if (s->flags & MTA_FORCE_ANYSSL) { - s->use_smtps = 1; /* tls+smtps */ - break; - } - else if (s->flags & MTA_DOWNGRADE_PLAIN) { - /* smtp, with tls failure */ - break; - } - default: - mta_free(s); - return; - } - portno = s->use_smtps ? 465 : 25; - - /* Override with relay-specified port */ - if (s->relay->port) - portno = s->relay->port; - - memmove(&ss, s->route->dst->sa, SA_LEN(s->route->dst->sa)); - sa = (struct sockaddr *)&ss; - - if (sa->sa_family == AF_INET) - ((struct sockaddr_in *)sa)->sin_port = htons(portno); - else if (sa->sa_family == AF_INET6) - ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); - - s->attempt += 1; - if (s->use_smtp_tls) - schema = "smtp://"; - else if (s->use_starttls) - schema = "smtp+tls://"; - else if (s->use_smtps) - schema = "smtps://"; - else if (s->flags & MTA_LMTP) - schema = "lmtp://"; - else - schema = "smtp+notls://"; - - log_info("%016"PRIx64" mta " - "connecting address=%s%s:%d host=%s", - s->id, schema, sa_to_text(s->route->dst->sa), - portno, s->route->dst->ptrname); - - mta_enter_state(s, MTA_INIT); - s->io = io_new(); - io_set_callback(s->io, mta_io, s); - io_set_timeout(s->io, 300000); - if (io_connect(s->io, sa, s->route->src->sa) == -1) { - /* - * This error is most likely a "no route", - * so there is no need to try again. - */ - log_debug("debug: mta: io_connect failed: %s", io_error(s->io)); - if (errno == EADDRNOTAVAIL) - mta_source_error(s->relay, s->route, io_error(s->io)); - else - mta_error(s, "Connection failed: %s", io_error(s->io)); - mta_free(s); - } -} - -static void -mta_enter_state(struct mta_session *s, int newstate) -{ - struct mta_envelope *e; - size_t envid_sz; - int oldstate; - ssize_t q; - char ibuf[LINE_MAX]; - char obuf[LINE_MAX]; - int offset; - const char *srs_sender; - -again: - oldstate = s->state; - - log_trace(TRACE_MTA, "mta: %p: %s -> %s", s, - mta_strstate(oldstate), - mta_strstate(newstate)); - - s->state = newstate; - - memset(s->replybuf, 0, sizeof s->replybuf); - - /* don't try this at home! */ -#define mta_enter_state(_s, _st) do { newstate = _st; goto again; } while (0) - - switch (s->state) { - case MTA_INIT: - case MTA_BANNER: - break; - - case MTA_EHLO: - s->ext = 0; - mta_send(s, "EHLO %s", s->helo); - mta_report_link_identify(s, "EHLO", s->helo); - break; - - case MTA_HELO: - s->ext = 0; - mta_send(s, "HELO %s", s->helo); - mta_report_link_identify(s, "HELO", s->helo); - break; - - case MTA_LHLO: - s->ext = 0; - mta_send(s, "LHLO %s", s->helo); - mta_report_link_identify(s, "LHLO", s->helo); - break; - - case MTA_STARTTLS: - if (s->flags & MTA_DOWNGRADE_PLAIN) - mta_enter_state(s, MTA_AUTH); - if (s->flags & MTA_TLS) /* already started */ - mta_enter_state(s, MTA_AUTH); - else if ((s->ext & MTA_EXT_STARTTLS) == 0) { - if (s->flags & MTA_FORCE_TLS || s->flags & MTA_WANT_SECURE) { - mta_error(s, "TLS required but not supported by remote host"); - s->flags |= MTA_RECONN; - } - else - /* server doesn't support starttls, do not use it */ - mta_enter_state(s, MTA_AUTH); - } - else - mta_send(s, "STARTTLS"); - break; - - case MTA_AUTH: - if (s->relay->secret && s->flags & MTA_TLS) { - if (s->ext & MTA_EXT_AUTH) { - if (s->ext & MTA_EXT_AUTH_PLAIN) { - mta_enter_state(s, MTA_AUTH_PLAIN); - break; - } - if (s->ext & MTA_EXT_AUTH_LOGIN) { - mta_enter_state(s, MTA_AUTH_LOGIN); - break; - } - log_debug("debug: mta: %p: no supported AUTH method on session", s); - mta_error(s, "no supported AUTH method"); - } - else { - log_debug("debug: mta: %p: AUTH not advertised on session", s); - mta_error(s, "AUTH not advertised"); - } - } - else if (s->relay->secret) { - log_debug("debug: mta: %p: not using AUTH on non-TLS " - "session", s); - mta_error(s, "Refuse to AUTH over unsecure channel"); - mta_connect(s); - } else { - mta_enter_state(s, MTA_READY); - } - break; - - case MTA_AUTH_PLAIN: - memset(ibuf, 0, sizeof ibuf); - if (base64_decode(s->relay->secret, (unsigned char *)ibuf, - sizeof(ibuf)-1) == -1) { - log_debug("debug: mta: %p: credentials too large on session", s); - mta_error(s, "Credentials too large"); - break; - } - s->username = xstrdup(ibuf+1); - mta_send(s, "AUTH PLAIN %s", s->relay->secret); - break; - - case MTA_AUTH_LOGIN: - mta_send(s, "AUTH LOGIN"); - break; - - case MTA_AUTH_LOGIN_USER: - memset(ibuf, 0, sizeof ibuf); - if (base64_decode(s->relay->secret, (unsigned char *)ibuf, - sizeof(ibuf)-1) == -1) { - log_debug("debug: mta: %p: credentials too large on session", s); - mta_error(s, "Credentials too large"); - break; - } - s->username = xstrdup(ibuf+1); - - memset(obuf, 0, sizeof obuf); - base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf); - mta_send(s, "%s", obuf); - - memset(ibuf, 0, sizeof ibuf); - memset(obuf, 0, sizeof obuf); - break; - - case MTA_AUTH_LOGIN_PASS: - memset(ibuf, 0, sizeof ibuf); - if (base64_decode(s->relay->secret, (unsigned char *)ibuf, - sizeof(ibuf)-1) == -1) { - log_debug("debug: mta: %p: credentials too large on session", s); - mta_error(s, "Credentials too large"); - break; - } - - offset = strlen(ibuf+1)+2; - memset(obuf, 0, sizeof obuf); - base64_encode((unsigned char *)ibuf + offset, strlen(ibuf + offset), obuf, sizeof obuf); - mta_send(s, "%s", obuf); - - memset(ibuf, 0, sizeof ibuf); - memset(obuf, 0, sizeof obuf); - break; - - case MTA_READY: - /* Ready to send a new mail */ - if (s->ready == 0) { - s->ready = 1; - s->relay->nconn_ready += 1; - mta_route_ok(s->relay, s->route); - } - - if (s->msgtried >= MAX_TRYBEFOREDISABLE) { - log_info("%016"PRIx64" mta host-rejects-all-mails", - s->id); - mta_route_down(s->relay, s->route); - mta_enter_state(s, MTA_QUIT); - break; - } - - if (s->msgcount >= s->relay->limits->max_mail_per_session) { - log_debug("debug: mta: " - "%p: cannot send more message to relay %s", s, - mta_relay_to_text(s->relay)); - mta_enter_state(s, MTA_QUIT); - break; - } - - /* - * When downgrading from opportunistic TLS, clear flag and - * possibly reuse the same task (forbidden in other cases). - */ - if (s->flags & MTA_DOWNGRADE_PLAIN) - s->flags &= ~MTA_DOWNGRADE_PLAIN; - else if (s->task) - fatalx("task should be NULL at this point"); - - if (s->task == NULL) - s->task = mta_route_next_task(s->relay, s->route); - if (s->task == NULL) { - log_debug("debug: mta: %p: no task for relay %s", - s, mta_relay_to_text(s->relay)); - - if (s->relay->nconn > 1 || - s->hangon >= s->relay->limits->sessdelay_keepalive) { - mta_enter_state(s, MTA_QUIT); - break; - } - - log_debug("mta: debug: last connection: hanging on for %llds", - (long long)(s->relay->limits->sessdelay_keepalive - - s->hangon)); - s->flags |= MTA_HANGON; - runq_schedule(hangon, 1, s); - break; - } - - log_debug("debug: mta: %p: handling next task for relay %s", s, - mta_relay_to_text(s->relay)); - - stat_increment("mta.task.running", 1); - - m_create(p_queue, IMSG_MTA_OPEN_MESSAGE, 0, 0, -1); - m_add_id(p_queue, s->id); - m_add_msgid(p_queue, s->task->msgid); - m_close(p_queue); - - tree_xset(&wait_fd, s->id, s); - s->flags |= MTA_WAIT; - break; - - case MTA_MAIL: - s->currevp = TAILQ_FIRST(&s->task->envelopes); - - e = s->currevp; - s->hangon = 0; - s->msgtried++; - envid_sz = strlen(e->dsn_envid); - - /* SRS-encode if requested for the relay action, AND we're not - * bouncing, AND we have an RCPT which means we are forwarded, - * AND the RCPT has a '@' just for sanity check (will always). - */ - if (env->sc_srs_key != NULL && - s->relay->srs && - strchr(s->task->sender, '@') && - e->rcpt && - strchr(e->rcpt, '@')) { - /* encode and replace task sender with new SRS-sender */ - srs_sender = srs_encode(s->task->sender, - strchr(e->rcpt, '@') + 1); - if (srs_sender) { - free(s->task->sender); - s->task->sender = xstrdup(srs_sender); - } - } - - if (s->ext & MTA_EXT_DSN) { - mta_send(s, "MAIL FROM:<%s>%s%s%s%s", - s->task->sender, - e->dsn_ret ? " RET=" : "", - e->dsn_ret ? dsn_strret(e->dsn_ret) : "", - envid_sz ? " ENVID=" : "", - envid_sz ? e->dsn_envid : ""); - } else - mta_send(s, "MAIL FROM:<%s>", s->task->sender); - break; - - case MTA_RCPT: - if (s->currevp == NULL) - s->currevp = TAILQ_FIRST(&s->task->envelopes); - - e = s->currevp; - if (s->ext & MTA_EXT_DSN) { - mta_send(s, "RCPT TO:<%s>%s%s%s%s", - e->dest, - e->dsn_notify ? " NOTIFY=" : "", - e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "", - e->dsn_orcpt ? " ORCPT=rfc822;" : "", - e->dsn_orcpt ? e->dsn_orcpt : ""); - } else - mta_send(s, "RCPT TO:<%s>", e->dest); - - mta_report_tx_envelope(s, s->task->msgid, e->id); - s->rcptcount++; - break; - - case MTA_DATA: - fseek(s->datafp, 0, SEEK_SET); - mta_send(s, "DATA"); - break; - - case MTA_BODY: - if (s->datafp == NULL) { - log_trace(TRACE_MTA, "mta: %p: end-of-file", s); - mta_enter_state(s, MTA_EOM); - break; - } - - if ((q = mta_queue_data(s)) == -1) { - s->flags |= MTA_FREE; - break; - } - if (q == 0) { - mta_enter_state(s, MTA_BODY); - break; - } - - log_trace(TRACE_MTA, "mta: %p: >>> [...%zd bytes...]", s, q); - break; - - case MTA_EOM: - mta_send(s, "."); - break; - - case MTA_LMTP_EOM: - /* LMTP reports status of each delivery, so enable read */ - io_set_read(s->io); - break; - - case MTA_RSET: - if (s->datafp) { - fclose(s->datafp); - s->datafp = NULL; - s->datalen = 0; - } - mta_send(s, "RSET"); - break; - - case MTA_QUIT: - mta_send(s, "QUIT"); - break; - - default: - fatalx("mta_enter_state: unknown state"); - } -#undef mta_enter_state -} - -/* - * Handle a response to an SMTP command - */ -static void -mta_response(struct mta_session *s, char *line) -{ - struct mta_envelope *e; - struct sockaddr_storage ss; - struct sockaddr *sa; - const char *domain; - char *pbuf; - socklen_t sa_len; - char buf[LINE_MAX]; - int delivery; - - switch (s->state) { - - case MTA_BANNER: - if (line[0] != '2') { - mta_error(s, "BANNER rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - - pbuf = ""; - if (strlen(line) > 4) { - (void)strlcpy(buf, line + 4, sizeof buf); - if ((pbuf = strchr(buf, ' '))) - *pbuf = '\0'; - pbuf = valid_domainpart(buf) ? buf : ""; - } - mta_report_link_greeting(s, pbuf); - - if (s->flags & MTA_LMTP) - mta_enter_state(s, MTA_LHLO); - else - mta_enter_state(s, MTA_EHLO); - break; - - case MTA_EHLO: - if (line[0] != '2') { - /* rejected at ehlo state */ - if ((s->relay->flags & RELAY_AUTH) || - (s->flags & MTA_WANT_SECURE)) { - mta_error(s, "EHLO rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_HELO); - return; - } - if (!(s->flags & MTA_FORCE_PLAIN)) - mta_enter_state(s, MTA_STARTTLS); - else - mta_enter_state(s, MTA_READY); - break; - - case MTA_HELO: - if (line[0] != '2') { - mta_error(s, "HELO rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_READY); - break; - - case MTA_LHLO: - if (line[0] != '2') { - mta_error(s, "LHLO rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_READY); - break; - - case MTA_STARTTLS: - if (line[0] != '2') { - if (!(s->flags & MTA_WANT_SECURE)) { - mta_enter_state(s, MTA_AUTH); - return; - } - /* XXX mark that the MX doesn't support STARTTLS */ - mta_error(s, "STARTTLS rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - - mta_cert_init(s); - break; - - case MTA_AUTH_PLAIN: - if (line[0] != '2') { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_report_link_auth(s, s->username, "pass"); - mta_enter_state(s, MTA_READY); - break; - - case MTA_AUTH_LOGIN: - if (strncmp(line, "334 ", 4) != 0) { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_AUTH_LOGIN_USER); - break; - - case MTA_AUTH_LOGIN_USER: - if (strncmp(line, "334 ", 4) != 0) { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_AUTH_LOGIN_PASS); - break; - - case MTA_AUTH_LOGIN_PASS: - if (line[0] != '2') { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_report_link_auth(s, s->username, "pass"); - mta_enter_state(s, MTA_READY); - break; - - case MTA_MAIL: - if (line[0] != '2') { - if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - - mta_flush_task(s, delivery, line, 0, 0); - mta_enter_state(s, MTA_RSET); - return; - } - mta_report_tx_begin(s, s->task->msgid); - mta_report_tx_mail(s, s->task->msgid, s->task->sender, 1); - mta_enter_state(s, MTA_RCPT); - break; - - case MTA_RCPT: - e = s->currevp; - - /* remove envelope from hosttat cache if there */ - if ((domain = strchr(e->dest, '@')) != NULL) { - domain++; - mta_hoststat_uncache(domain, e->id); - } - - s->currevp = TAILQ_NEXT(s->currevp, entry); - if (line[0] == '2') { - s->failures = 0; - /* - * this host is up, reschedule envelopes that - * were cached for reschedule. - */ - if (domain) - mta_hoststat_reschedule(domain); - } - else { - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - s->failures++; - - /* remove failed envelope from task list */ - TAILQ_REMOVE(&s->task->envelopes, e, entry); - stat_decrement("mta.envelope", 1); - - /* log right away */ - (void)snprintf(buf, sizeof(buf), "%s", - mta_host_to_text(s->route->dst)); - - e->session = s->id; - /* XXX */ - /* - * getsockname() can only fail with ENOBUFS here - * best effort, don't log source ... - */ - sa_len = sizeof(ss); - sa = (struct sockaddr *)&ss; - if (getsockname(io_fileno(s->io), sa, &sa_len) == -1) - mta_delivery_log(e, NULL, buf, delivery, line); - else - mta_delivery_log(e, sa_to_text(sa), - buf, delivery, line); - - if (domain) - mta_hoststat_update(domain, e->status); - mta_delivery_notify(e); - - if (s->relay->limits->max_failures_per_session && - s->failures == s->relay->limits->max_failures_per_session) { - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Too many consecutive errors, closing connection", 0, 1); - mta_enter_state(s, MTA_QUIT); - break; - } - - /* - * if no more envelopes, flush failed queue - */ - if (TAILQ_EMPTY(&s->task->envelopes)) { - mta_flush_task(s, IMSG_MTA_DELIVERY_OK, - "No envelope", 0, 0); - mta_enter_state(s, MTA_RSET); - break; - } - } - - switch (line[0]) { - case '2': - mta_report_tx_rcpt(s, - s->task->msgid, e->dest, 1); - break; - case '4': - mta_report_tx_rcpt(s, - s->task->msgid, e->dest, -1); - break; - case '5': - mta_report_tx_rcpt(s, - s->task->msgid, e->dest, 0); - break; - } - - if (s->currevp == NULL) - mta_enter_state(s, MTA_DATA); - else - mta_enter_state(s, MTA_RCPT); - break; - - case MTA_DATA: - if (line[0] == '2' || line[0] == '3') { - mta_report_tx_data(s, s->task->msgid, 1); - mta_enter_state(s, MTA_BODY); - break; - } - - if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - mta_report_tx_data(s, s->task->msgid, - delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0); - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - mta_flush_task(s, delivery, line, 0, 0); - mta_enter_state(s, MTA_RSET); - break; - - case MTA_LMTP_EOM: - case MTA_EOM: - if (line[0] == '2') { - delivery = IMSG_MTA_DELIVERY_OK; - s->msgtried = 0; - s->msgcount++; - } - else if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - if (delivery != IMSG_MTA_DELIVERY_OK) { - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - } - else { - mta_report_tx_commit(s, s->task->msgid, s->datalen); - mta_report_tx_reset(s, s->task->msgid); - } - mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0); - if (s->task) { - s->rcptcount--; - mta_enter_state(s, MTA_LMTP_EOM); - } else { - s->rcptcount = 0; - if (s->relay->limits->sessdelay_transaction) { - log_debug("debug: mta: waiting for %llds before next transaction", - (long long int)s->relay->limits->sessdelay_transaction); - s->hangon = s->relay->limits->sessdelay_transaction -1; - s->flags |= MTA_HANGON; - runq_schedule(hangon, - s->relay->limits->sessdelay_transaction, s); - } - else - mta_enter_state(s, MTA_READY); - } - break; - - case MTA_RSET: - s->rcptcount = 0; - - if (s->task) { - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - } - if (s->relay->limits->sessdelay_transaction) { - log_debug("debug: mta: waiting for %llds after reset", - (long long int)s->relay->limits->sessdelay_transaction); - s->hangon = s->relay->limits->sessdelay_transaction -1; - s->flags |= MTA_HANGON; - runq_schedule(hangon, - s->relay->limits->sessdelay_transaction, s); - } - else - mta_enter_state(s, MTA_READY); - break; - - default: - fatalx("mta_response() bad state"); - } -} - -static void -mta_io(struct io *io, int evt, void *arg) -{ - struct mta_session *s = arg; - char *line, *msg, *p; - size_t len; - const char *error; - int cont; - - log_trace(TRACE_IO, "mta: %p: %s %s", s, io_strevent(evt), - io_strio(io)); - - switch (evt) { - - case IO_CONNECTED: - mta_connected(s); - - if (s->use_smtps) { - io_set_write(io); - mta_cert_init(s); - } - else { - mta_enter_state(s, MTA_BANNER); - io_set_read(io); - } - break; - - case IO_TLSREADY: - log_info("%016"PRIx64" mta tls ciphers=%s", - s->id, ssl_to_text(io_tls(s->io))); - s->flags |= MTA_TLS; - - mta_report_link_tls(s, - ssl_to_text(io_tls(s->io))); - - mta_cert_verify(s); - break; - - case IO_DATAIN: - nextline: - line = io_getline(s->io, &len); - if (line == NULL) { - if (io_datalen(s->io) >= LINE_MAX) { - mta_error(s, "Input too long"); - mta_free(s); - } - return; - } - - /* Strip trailing '\r' */ - if (len && line[len - 1] == '\r') - line[--len] = '\0'; - - log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line); - mta_report_protocol_server(s, line); - - if ((error = parse_smtp_response(line, len, &msg, &cont))) { - mta_error(s, "Bad response: %s", error); - mta_free(s); - return; - } - - /* read extensions */ - if (s->state == MTA_EHLO) { - if (strcmp(msg, "STARTTLS") == 0) - s->ext |= MTA_EXT_STARTTLS; - else if (strncmp(msg, "AUTH ", 5) == 0) { - s->ext |= MTA_EXT_AUTH; - if ((p = strstr(msg, " PLAIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - s->ext |= MTA_EXT_AUTH_PLAIN; - if ((p = strstr(msg, " LOGIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - s->ext |= MTA_EXT_AUTH_LOGIN; - } - else if (strcmp(msg, "PIPELINING") == 0) - s->ext |= MTA_EXT_PIPELINING; - else if (strcmp(msg, "DSN") == 0) - s->ext |= MTA_EXT_DSN; - else if (strncmp(msg, "SIZE ", 5) == 0) { - s->ext_size = strtonum(msg+5, 0, UINT32_MAX, &error); - if (error == NULL) - s->ext |= MTA_EXT_SIZE; - } - } - - /* continuation reply, we parse out the repeating statuses and ESC */ - if (cont) { - if (s->replybuf[0] == '\0') - (void)strlcat(s->replybuf, line, sizeof s->replybuf); - else if (len > 4) { - p = line + 4; - if (isdigit((unsigned char)p[0]) && p[1] == '.' && - isdigit((unsigned char)p[2]) && p[3] == '.' && - isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5])) - p += 5; - (void)strlcat(s->replybuf, p, sizeof s->replybuf); - } - goto nextline; - } - - /* last line of a reply, check if we're on a continuation to parse out status and ESC. - * if we overflow reply buffer or are not on continuation, log entire last line. - */ - if (s->replybuf[0] == '\0') - (void)strlcat(s->replybuf, line, sizeof s->replybuf); - else if (len > 4) { - p = line + 4; - if (isdigit((unsigned char)p[0]) && p[1] == '.' && - isdigit((unsigned char)p[2]) && p[3] == '.' && - isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5])) - p += 5; - if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf) - (void)strlcpy(s->replybuf, line, sizeof s->replybuf); - } - - if (s->state == MTA_QUIT) { - log_info("%016"PRIx64" mta disconnected reason=quit messages=%zu", - s->id, s->msgcount); - mta_free(s); - return; - } - io_set_write(io); - mta_response(s, s->replybuf); - if (s->flags & MTA_FREE) { - mta_free(s); - return; - } - if (s->flags & MTA_RECONN) { - s->flags &= ~MTA_RECONN; - mta_connect(s); - return; - } - - if (io_datalen(s->io)) { - log_debug("debug: mta: remaining data in input buffer"); - mta_error(s, "Remote host sent too much data"); - if (s->flags & MTA_WAIT) - s->flags |= MTA_FREE; - else - mta_free(s); - } - break; - - case IO_LOWAT: - if (s->state == MTA_BODY) { - mta_enter_state(s, MTA_BODY); - if (s->flags & MTA_FREE) { - mta_free(s); - return; - } - } - - if (io_queued(s->io) == 0) - io_set_read(io); - break; - - case IO_TIMEOUT: - log_debug("debug: mta: %p: connection timeout", s); - mta_error(s, "Connection timeout"); - mta_report_timeout(s); - if (!s->ready) - mta_connect(s); - else - mta_free(s); - break; - - case IO_ERROR: - case IO_TLSERROR: - log_debug("debug: mta: %p: IO error: %s", s, io_error(io)); - - if (s->state == MTA_STARTTLS && s->use_smtp_tls) { - /* error in non-strict SSL negotiation, downgrade to plain */ - log_info("smtp-out: Error on session %016"PRIx64 - ": opportunistic TLS failed, " - "downgrading to plain", s->id); - s->flags &= ~MTA_TLS; - s->flags |= MTA_DOWNGRADE_PLAIN; - mta_connect(s); - break; - } - - mta_error(s, "IO Error: %s", io_error(io)); - mta_free(s); - break; - - case IO_DISCONNECTED: - log_debug("debug: mta: %p: disconnected in state %s", - s, mta_strstate(s->state)); - mta_error(s, "Connection closed unexpectedly"); - if (!s->ready) - mta_connect(s); - else - mta_free(s); - break; - - default: - fatalx("mta_io() bad event"); - } -} - -static void -mta_send(struct mta_session *s, char *fmt, ...) -{ - va_list ap; - char *p; - int len; - - va_start(ap, fmt); - if ((len = vasprintf(&p, fmt, ap)) == -1) - fatal("mta: vasprintf"); - va_end(ap); - - log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p); - - if (strncasecmp(p, "AUTH PLAIN ", 11) == 0) - mta_report_protocol_client(s, "AUTH PLAIN ********"); - else if (s->state == MTA_AUTH_LOGIN_USER || s->state == MTA_AUTH_LOGIN_PASS) - mta_report_protocol_client(s, "********"); - else - mta_report_protocol_client(s, p); - - io_xprintf(s->io, "%s\r\n", p); - - free(p); -} - -/* - * Queue some data into the input buffer - */ -static ssize_t -mta_queue_data(struct mta_session *s) -{ - char *ln = NULL; - size_t sz = 0, q; - ssize_t len; - - q = io_queued(s->io); - - while (io_queued(s->io) < MTA_HIWAT) { - if ((len = getline(&ln, &sz, s->datafp)) == -1) - break; - if (ln[len - 1] == '\n') - ln[len - 1] = '\0'; - s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln); - } - - free(ln); - if (ferror(s->datafp)) { - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Error reading content file", 0, 0); - return (-1); - } - - if (feof(s->datafp)) { - fclose(s->datafp); - s->datafp = NULL; - } - - return (io_queued(s->io) - q); -} - -static void -mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t count, - int cache) -{ - struct mta_envelope *e; - char relay[LINE_MAX]; - size_t n; - struct sockaddr_storage ss; - struct sockaddr *sa; - socklen_t sa_len; - const char *domain; - - (void)snprintf(relay, sizeof relay, "%s", mta_host_to_text(s->route->dst)); - n = 0; - while ((e = TAILQ_FIRST(&s->task->envelopes))) { - - if (count && n == count) { - stat_decrement("mta.envelope", n); - return; - } - - TAILQ_REMOVE(&s->task->envelopes, e, entry); - - /* we're about to log, associate session to envelope */ - e->session = s->id; - e->ext = s->ext; - - /* XXX */ - /* - * getsockname() can only fail with ENOBUFS here - * best effort, don't log source ... - */ - sa = (struct sockaddr *)&ss; - sa_len = sizeof(ss); - if (getsockname(io_fileno(s->io), sa, &sa_len) == -1) - mta_delivery_log(e, NULL, relay, delivery, error); - else - mta_delivery_log(e, sa_to_text(sa), - relay, delivery, error); - - mta_delivery_notify(e); - - domain = strchr(e->dest, '@'); - if (domain) { - domain++; - mta_hoststat_update(domain, error); - if (cache) - mta_hoststat_cache(domain, e->id); - } - - n++; - } - - free(s->task->sender); - free(s->task); - s->task = NULL; - - if (s->datafp) { - fclose(s->datafp); - s->datafp = NULL; - } - - stat_decrement("mta.envelope", n); - stat_decrement("mta.task.running", 1); - stat_decrement("mta.task", 1); -} - -static void -mta_error(struct mta_session *s, const char *fmt, ...) -{ - va_list ap; - char *error; - int len; - - va_start(ap, fmt); - if ((len = vasprintf(&error, fmt, ap)) == -1) - fatal("mta: vasprintf"); - va_end(ap); - - if (s->msgcount) - log_info("smtp-out: Error on session %016"PRIx64 - " after %zu message%s sent: %s", s->id, s->msgcount, - (s->msgcount > 1) ? "s" : "", error); - else - log_info("%016"PRIx64" mta error reason=%s", - s->id, error); - - /* - * If not connected yet, and the error is not local, just ignore it - * and try to reconnect. - */ - if (s->state == MTA_INIT && - (errno == ETIMEDOUT || errno == ECONNREFUSED)) { - log_debug("debug: mta: not reporting route error yet"); - free(error); - return; - } - - mta_route_error(s->relay, s->route); - - if (s->task) - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, error, 0, 0); - - free(error); -} - -static void -mta_cert_init(struct mta_session *s) -{ - const char *name; - int fallback; - - if (s->relay->pki_name) { - name = s->relay->pki_name; - fallback = 0; - } - else { - name = s->helo; - fallback = 1; - } - - if (cert_init(name, fallback, mta_cert_init_cb, s)) { - tree_xset(&wait_tls_init, s->id, s); - s->flags |= MTA_WAIT; - } -} - -static void -mta_cert_init_cb(void *arg, int status, const char *name, const void *cert, - size_t cert_len) -{ - struct mta_session *s = arg; - void *ssl; - char *xname = NULL, *xcert = NULL; - - if (s->flags & MTA_WAIT) - mta_tree_pop(&wait_tls_init, s->id); - - if (status == CA_FAIL && s->relay->pki_name) { - log_info("%016"PRIx64" mta closing reason=ca-failure", s->id); - mta_free(s); - return; - } - - if (name) - xname = xstrdup(name); - if (cert) - xcert = xmemdup(cert, cert_len); - ssl = ssl_mta_init(xname, xcert, cert_len, env->sc_tls_ciphers); - free(xname); - free(xcert); - if (ssl == NULL) - fatal("mta: ssl_mta_init"); - io_start_tls(s->io, ssl); -} - -static void -mta_cert_verify(struct mta_session *s) -{ - const char *name; - int fallback; - - if (s->relay->ca_name) { - name = s->relay->ca_name; - fallback = 0; - } - else { - name = s->helo; - fallback = 1; - } - - if (cert_verify(io_tls(s->io), name, fallback, mta_cert_verify_cb, s)) { - tree_xset(&wait_tls_verify, s->id, s); - io_pause(s->io, IO_IN); - s->flags |= MTA_WAIT; - } -} - -static void -mta_cert_verify_cb(void *arg, int status) -{ - struct mta_session *s = arg; - int match, resume = 0; - X509 *cert; - - if (s->flags & MTA_WAIT) { - mta_tree_pop(&wait_tls_verify, s->id); - resume = 1; - } - - if (status == CERT_OK) { - cert = SSL_get_peer_certificate(io_tls(s->io)); - if (!cert) - status = CERT_NOCERT; - else { - match = 0; - (void)ssl_check_name(cert, s->mxname, &match); - X509_free(cert); - if (!match) { - log_info("%016"PRIx64" mta " - "ssl_check_name: no match for '%s' in cert", - s->id, s->mxname); - status = CERT_INVALID; - } - } - } - - if (status == CERT_OK) - s->flags |= MTA_TLS_VERIFIED; - else if (s->relay->flags & RELAY_TLS_VERIFY) { - errno = 0; - mta_error(s, "SSL certificate check failed"); - mta_free(s); - return; - } - - mta_tls_verified(s); - if (resume) - io_resume(s->io, IO_IN); -} - -static void -mta_tls_verified(struct mta_session *s) -{ - X509 *x; - - x = SSL_get_peer_certificate(io_tls(s->io)); - if (x) { - log_info("%016"PRIx64" mta " - "server-cert-check result=\"%s\"", - s->id, - (s->flags & MTA_TLS_VERIFIED) ? "success" : "failure"); - X509_free(x); - } - - if (s->use_smtps) { - mta_enter_state(s, MTA_BANNER); - io_set_read(s->io); - } - else - mta_enter_state(s, MTA_EHLO); -} - -static const char * -dsn_strret(enum dsn_ret ret) -{ - if (ret == DSN_RETHDRS) - return "HDRS"; - else if (ret == DSN_RETFULL) - return "FULL"; - else { - log_debug("mta: invalid ret %d", ret); - return "???"; - } -} - -static const char * -dsn_strnotify(uint8_t arg) -{ - static char buf[32]; - size_t sz; - - buf[0] = '\0'; - if (arg & DSN_SUCCESS) - (void)strlcat(buf, "SUCCESS,", sizeof(buf)); - - if (arg & DSN_FAILURE) - (void)strlcat(buf, "FAILURE,", sizeof(buf)); - - if (arg & DSN_DELAY) - (void)strlcat(buf, "DELAY,", sizeof(buf)); - - if (arg & DSN_NEVER) - (void)strlcat(buf, "NEVER,", sizeof(buf)); - - /* trim trailing comma */ - sz = strlen(buf); - if (sz) - buf[sz - 1] = '\0'; - - return (buf); -} - -#define CASE(x) case x : return #x - -static const char * -mta_strstate(int state) -{ - switch (state) { - CASE(MTA_INIT); - CASE(MTA_BANNER); - CASE(MTA_EHLO); - CASE(MTA_HELO); - CASE(MTA_STARTTLS); - CASE(MTA_AUTH); - CASE(MTA_AUTH_PLAIN); - CASE(MTA_AUTH_LOGIN); - CASE(MTA_AUTH_LOGIN_USER); - CASE(MTA_AUTH_LOGIN_PASS); - CASE(MTA_READY); - CASE(MTA_MAIL); - CASE(MTA_RCPT); - CASE(MTA_DATA); - CASE(MTA_BODY); - CASE(MTA_EOM); - CASE(MTA_LMTP_EOM); - CASE(MTA_RSET); - CASE(MTA_QUIT); - default: - return "MTA_???"; - } -} - -static void -mta_filter_begin(struct mta_session *s) -{ - if (!SESSION_FILTERED(s)) - return; - - m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->relay->dispatcher->u.remote.filtername); - m_close(p_lka); -} - -static void -mta_filter_end(struct mta_session *s) -{ - if (!SESSION_FILTERED(s)) - return; - - m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1); - m_add_id(p_lka, s->id); - m_close(p_lka); -} - -static void -mta_connected(struct mta_session *s) -{ - struct sockaddr_storage sa_src; - struct sockaddr_storage sa_dest; - int sa_len; - - log_info("%016"PRIx64" mta connected", s->id); - - sa_len = sizeof sa_src; - if (getsockname(io_fileno(s->io), - (struct sockaddr *)&sa_src, &sa_len) == -1) - bzero(&sa_src, sizeof sa_src); - sa_len = sizeof sa_dest; - if (getpeername(io_fileno(s->io), - (struct sockaddr *)&sa_dest, &sa_len) == -1) - bzero(&sa_dest, sizeof sa_dest); - - mta_report_link_connect(s, - s->route->dst->ptrname, 1, - &sa_src, - &sa_dest); -} - -static void -mta_disconnected(struct mta_session *s) -{ - mta_report_link_disconnect(s); - mta_filter_end(s); -} - - -static void -mta_report_link_connect(struct mta_session *s, const char *rdns, int fcrdns, - const struct sockaddr_storage *ss_src, - const struct sockaddr_storage *ss_dest) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_connect("smtp-out", s->id, rdns, fcrdns, ss_src, ss_dest); -} - -static void -mta_report_link_greeting(struct mta_session *s, - const char *domain) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_greeting("smtp-out", s->id, domain); -} - -static void -mta_report_link_identify(struct mta_session *s, const char *method, const char *identity) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_identify("smtp-out", s->id, method, identity); -} - -static void -mta_report_link_tls(struct mta_session *s, const char *ssl) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_tls("smtp-out", s->id, ssl); -} - -static void -mta_report_link_disconnect(struct mta_session *s) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_disconnect("smtp-out", s->id); -} - -static void -mta_report_link_auth(struct mta_session *s, const char *user, const char *result) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_auth("smtp-out", s->id, user, result); -} - -static void -mta_report_tx_reset(struct mta_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_reset("smtp-out", s->id, msgid); -} - -static void -mta_report_tx_begin(struct mta_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_begin("smtp-out", s->id, msgid); -} - -static void -mta_report_tx_mail(struct mta_session *s, uint32_t msgid, const char *address, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_mail("smtp-out", s->id, msgid, address, ok); -} - -static void -mta_report_tx_rcpt(struct mta_session *s, uint32_t msgid, const char *address, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_rcpt("smtp-out", s->id, msgid, address, ok); -} - -static void -mta_report_tx_envelope(struct mta_session *s, uint32_t msgid, uint64_t evpid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_envelope("smtp-out", s->id, msgid, evpid); -} - -static void -mta_report_tx_data(struct mta_session *s, uint32_t msgid, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_data("smtp-out", s->id, msgid, ok); -} - -static void -mta_report_tx_commit(struct mta_session *s, uint32_t msgid, size_t msgsz) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_commit("smtp-out", s->id, msgid, msgsz); -} - -static void -mta_report_tx_rollback(struct mta_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_rollback("smtp-out", s->id, msgid); -} - -static void -mta_report_protocol_client(struct mta_session *s, const char *command) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_protocol_client("smtp-out", s->id, command); -} - -static void -mta_report_protocol_server(struct mta_session *s, const char *response) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_protocol_server("smtp-out", s->id, response); -} - -#if 0 -static void -mta_report_filter_response(struct mta_session *s, int phase, int response, const char *param) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_filter_response("smtp-out", s->id, phase, response, param); -} -#endif - -static void -mta_report_timeout(struct mta_session *s) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_timeout("smtp-out", s->id); -} diff --git a/smtpd/newaliases.8 b/smtpd/newaliases.8 deleted file mode 100644 index b82e4515..00000000 --- a/smtpd/newaliases.8 +++ /dev/null @@ -1,86 +0,0 @@ -.\" $OpenBSD: newaliases.8,v 1.12 2018/07/20 15:35:33 millert Exp $ -.\" -.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@openbsd.org> -.\" Copyright (c) 2008-2009 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: July 20 2018 $ -.Dt NEWALIASES 8 -.Os -.Sh NAME -.Nm newaliases -.Nd rebuild mail aliases -.Sh SYNOPSIS -.Nm newaliases -.Op Fl f Ar file -.Sh DESCRIPTION -The -.Nm -utility makes changes to the mail aliases file visible to -.Xr smtpd 8 . -It should be run every time the -.Xr aliases 5 -file is changed. -The location of the alias file is defined in -.Xr smtpd.conf 5 , -and defaults to -.Pa /etc/mail/aliases . -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl f Ar file -Use -.Ar file -as the configuration file, -instead of the default -.Pa /etc/mail/smtpd.conf . -.El -.Pp -If using database (db) files, -.Nm -is equivalent to running -.Xr makemap 8 -as follows: -.Bd -literal -offset indent -# makemap -t aliases /etc/mail/aliases -.Ed -.Pp -If using plain text files, -.Nm -is equivalent to running -.Xr smtpctl 8 -as follows: -.Bd -literal -offset indent -# smtpctl update table aliases -.Ed -.Sh FILES -.Bl -tag -width "/etc/mail/aliasesXXX" -compact -.It Pa /etc/mail/aliases -List of local user mail aliases. -.It Pa /etc/mail/virtual -List of virtual host aliases. -.El -.Sh EXIT STATUS -.Ex -std newaliases -.Sh SEE ALSO -.Xr smtpd.conf 5 , -.Xr makemap 8 , -.Xr smtpctl 8 , -.Xr smtpd 8 -.Sh HISTORY -The -.Nm -command first appeared in -.Ox 4.6 -as a replacement for the equivalent command shipped with sendmail. diff --git a/smtpd/parse.y b/smtpd/parse.y deleted file mode 100644 index db5be747..00000000 --- a/smtpd/parse.y +++ /dev/null @@ -1,3598 +0,0 @@ -/* $OpenBSD: parse.y,v 1.277 2020/02/24 23:54:27 millert Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. - * Copyright (c) 2001 Theo de Raadt. All rights reserved. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -%{ -#include "includes.h" - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/ioctl.h> - -#include <net/if.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <ifaddrs.h> -#include <imsg.h> -#include <inttypes.h> -#include <limits.h> -#include <netdb.h> -#include <pwd.h> -#include <stdarg.h> -#include <resolv.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <unistd.h> -#ifdef HAVE_UTIL_H -#include <util.h> -#endif - -#include <openssl/ssl.h> - -#include "smtpd.h" -#include "ssl.h" -#include "log.h" - -TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); -static struct file { - TAILQ_ENTRY(file) entry; - FILE *stream; - char *name; - size_t ungetpos; - size_t ungetsize; - u_char *ungetbuf; - int eof_reached; - int lineno; - int errors; -} *file, *topfile; -struct file *pushfile(const char *, int); -int popfile(void); -int check_file_secrecy(int, const char *); -int yyparse(void); -int yylex(void); -int kw_cmp(const void *, const void *); -int lookup(char *); -int igetc(void); -int lgetc(int); -void lungetc(int); -int findeol(void); -int yyerror(const char *, ...) - __attribute__((__format__ (printf, 1, 2))) - __attribute__((__nonnull__ (1))); - -TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); -struct sym { - TAILQ_ENTRY(sym) entry; - int used; - int persist; - char *nam; - char *val; -}; -int symset(const char *, const char *, int); -char *symget(const char *); - -struct smtpd *conf = NULL; -static int errors = 0; - -struct table *table = NULL; -struct mta_limits *limits; -static struct pki *pki; -static struct ca *sca; - -struct dispatcher *dispatcher; -struct rule *rule; -struct filter_proc *processor; -struct filter_config *filter_config; -static uint32_t last_dynchain_id = 1; - -enum listen_options { - LO_FAMILY = 0x000001, - LO_PORT = 0x000002, - LO_SSL = 0x000004, - LO_FILTER = 0x000008, - LO_PKI = 0x000010, - LO_AUTH = 0x000020, - LO_TAG = 0x000040, - LO_HOSTNAME = 0x000080, - LO_HOSTNAMES = 0x000100, - LO_MASKSOURCE = 0x000200, - LO_NODSN = 0x000400, - LO_SENDERS = 0x000800, - LO_RECEIVEDAUTH = 0x001000, - LO_MASQUERADE = 0x002000, - LO_CA = 0x004000, - LO_PROXY = 0x008000, -}; - -static struct listen_opts { - char *ifx; - int family; - in_port_t port; - uint16_t ssl; - char *filtername; - char *pki; - char *ca; - uint16_t auth; - struct table *authtable; - char *tag; - char *hostname; - struct table *hostnametable; - struct table *sendertable; - uint16_t flags; - - uint32_t options; -} listen_opts; - -static void create_sock_listener(struct listen_opts *); -static void create_if_listener(struct listen_opts *); -static void config_listener(struct listener *, struct listen_opts *); -static int host_v4(struct listen_opts *); -static int host_v6(struct listen_opts *); -static int host_dns(struct listen_opts *); -static int interface(struct listen_opts *); - -int delaytonum(char *); -int is_if_in_group(const char *, const char *); - -static int config_lo_mask_source(struct listen_opts *); - -typedef struct { - union { - int64_t number; - struct table *table; - char *string; - struct host *host; - struct mailaddr *maddr; - } v; - int lineno; -} YYSTYPE; - -%} - -%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL -%token BACKUP BOUNCE BYPASS -%token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT -%token DATA DATA_LINE DHE DISCONNECT DOMAIN -%token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY -%token FCRDNS FILTER FOR FORWARD_ONLY FROM -%token GROUP -%token HELO HELO_SRC HOST HOSTNAME HOSTNAMES -%token INCLUDE INET4 INET6 -%token JUNK -%token KEY -%token LIMIT LISTEN LMTP LOCAL -%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX -%token NO_DSN NO_VERIFY NOOP -%token ON -%token PHASE PKI PORT PROC PROC_EXEC PROXY_V2 -%token QUEUE QUIT -%token RCPT_TO RDNS RECIPIENT RECEIVEDAUTH REGEX RELAY REJECT REPORT REWRITE RSET -%token SCHEDULER SENDER SENDERS SMTP SMTP_IN SMTP_OUT SMTPS SOCKET SRC SRS SUB_ADDR_DELIM -%token TABLE TAG TAGGED TLS TLS_REQUIRE TTL -%token USER USERBASE -%token VERIFY VIRTUAL -%token WARN_INTERVAL WRAPPER - -%token <v.string> STRING -%token <v.number> NUMBER -%type <v.table> table -%type <v.number> size negation -%type <v.table> tables tablenew tableref -%% - -grammar : /* empty */ - | grammar '\n' - | grammar include '\n' - | grammar varset '\n' - | grammar bounce '\n' - | grammar ca '\n' - | grammar mda '\n' - | grammar mta '\n' - | grammar pki '\n' - | grammar proc '\n' - | grammar queue '\n' - | grammar scheduler '\n' - | grammar smtp '\n' - | grammar srs '\n' - | grammar listen '\n' - | grammar table '\n' - | grammar dispatcher '\n' - | grammar match '\n' - | grammar filter '\n' - | grammar error '\n' { file->errors++; } - ; - -include : INCLUDE STRING { - struct file *nfile; - - if ((nfile = pushfile($2, 0)) == NULL) { - yyerror("failed to include file %s", $2); - free($2); - YYERROR; - } - free($2); - - file = nfile; - lungetc('\n'); - } - ; - -varset : STRING '=' STRING { - char *s = $1; - while (*s++) { - if (isspace((unsigned char)*s)) { - yyerror("macro name cannot contain " - "whitespace"); - free($1); - free($3); - YYERROR; - } - } - if (symset($1, $3, 0) == -1) - fatal("cannot store variable"); - free($1); - free($3); - } - ; - -comma : ',' - | nl - | /* empty */ - ; - -optnl : '\n' optnl - | - ; - -nl : '\n' optnl - ; - -negation : '!' { $$ = 1; } - | /* empty */ { $$ = 0; } - ; - -assign : '=' | ARROW; - - -keyval : STRING assign STRING { - table_add(table, $1, $3); - free($1); - free($3); - } - ; - -keyval_list : keyval - | keyval comma keyval_list - ; - -stringel : STRING { - table_add(table, $1, NULL); - free($1); - } - ; - -string_list : stringel - | stringel comma string_list - ; - -tableval_list : string_list { } - | keyval_list { } - ; - -bounce: -BOUNCE WARN_INTERVAL { - memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn); -} bouncedelays -; - - -ca: -CA STRING { - char buf[HOST_NAME_MAX+1]; - - /* if not catchall, check that it is a valid domain */ - if (strcmp($2, "*") != 0) { - if (!res_hnok($2)) { - yyerror("not a valid domain name: %s", $2); - free($2); - YYERROR; - } - } - xlowercase(buf, $2, sizeof(buf)); - free($2); - sca = dict_get(conf->sc_ca_dict, buf); - if (sca == NULL) { - sca = xcalloc(1, sizeof *sca); - (void)strlcpy(sca->ca_name, buf, sizeof(sca->ca_name)); - dict_set(conf->sc_ca_dict, sca->ca_name, sca); - } -} ca_params -; - - -ca_params_opt: -CERT STRING { - sca->ca_cert_file = $2; -} -; - -ca_params: -ca_params_opt -; - - -mda: -MDA LIMIT limits_mda -| MDA WRAPPER STRING STRING { - if (dict_get(conf->sc_mda_wrappers, $3)) { - yyerror("mda wrapper already declared with that name: %s", $3); - YYERROR; - } - dict_set(conf->sc_mda_wrappers, $3, $4); -} -; - - -mta: -MTA MAX_DEFERRED NUMBER { - conf->sc_mta_max_deferred = $3; -} -| MTA LIMIT FOR DOMAIN STRING { - struct mta_limits *d; - - limits = dict_get(conf->sc_limits_dict, $5); - if (limits == NULL) { - limits = xcalloc(1, sizeof(*limits)); - dict_xset(conf->sc_limits_dict, $5, limits); - d = dict_xget(conf->sc_limits_dict, "default"); - memmove(limits, d, sizeof(*limits)); - } - free($5); -} limits_mta -| MTA LIMIT { - limits = dict_get(conf->sc_limits_dict, "default"); -} limits_mta -; - - -pki: -PKI STRING { - char buf[HOST_NAME_MAX+1]; - - /* if not catchall, check that it is a valid domain */ - if (strcmp($2, "*") != 0) { - if (!res_hnok($2)) { - yyerror("not a valid domain name: %s", $2); - free($2); - YYERROR; - } - } - xlowercase(buf, $2, sizeof(buf)); - free($2); - pki = dict_get(conf->sc_pki_dict, buf); - if (pki == NULL) { - pki = xcalloc(1, sizeof *pki); - (void)strlcpy(pki->pki_name, buf, sizeof(pki->pki_name)); - dict_set(conf->sc_pki_dict, pki->pki_name, pki); - } -} pki_params -; - -pki_params_opt: -CERT STRING { - pki->pki_cert_file = $2; -} -| KEY STRING { - pki->pki_key_file = $2; -} -| DHE STRING { - if (strcasecmp($2, "none") == 0) - pki->pki_dhe = 0; - else if (strcasecmp($2, "auto") == 0) - pki->pki_dhe = 1; - else if (strcasecmp($2, "legacy") == 0) - pki->pki_dhe = 2; - else { - yyerror("invalid DHE keyword: %s", $2); - free($2); - YYERROR; - } - free($2); -} -; - - -pki_params: -pki_params_opt pki_params -| /* empty */ -; - - -proc: -PROC STRING STRING { - if (dict_get(conf->sc_filter_processes_dict, $2)) { - yyerror("processor already exists with that name: %s", $2); - free($2); - free($3); - YYERROR; - } - processor = xcalloc(1, sizeof *processor); - processor->command = $3; -} proc_params { - dict_set(conf->sc_filter_processes_dict, $2, processor); - processor = NULL; -} -; - - -proc_params_opt: -USER STRING { - if (processor->user) { - yyerror("user already specified for this processor"); - free($2); - YYERROR; - } - processor->user = $2; -} -| GROUP STRING { - if (processor->group) { - yyerror("group already specified for this processor"); - free($2); - YYERROR; - } - processor->group = $2; -} -| CHROOT STRING { - if (processor->chroot) { - yyerror("chroot already specified for this processor"); - free($2); - YYERROR; - } - processor->chroot = $2; -} -; - -proc_params: -proc_params_opt proc_params -| /* empty */ -; - - -queue: -QUEUE COMPRESSION { - conf->sc_queue_flags |= QUEUE_COMPRESSION; -} -| QUEUE ENCRYPTION { - conf->sc_queue_flags |= QUEUE_ENCRYPTION; -} -| QUEUE ENCRYPTION STRING { - if (strcasecmp($3, "stdin") == 0 || strcasecmp($3, "-") == 0) { - conf->sc_queue_key = "stdin"; - free($3); - } - else - conf->sc_queue_key = $3; - conf->sc_queue_flags |= QUEUE_ENCRYPTION; -} -| QUEUE TTL STRING { - conf->sc_ttl = delaytonum($3); - if (conf->sc_ttl == -1) { - yyerror("invalid ttl delay: %s", $3); - free($3); - YYERROR; - } - free($3); -} -; - - -scheduler: -SCHEDULER LIMIT limits_scheduler -; - - -smtp: -SMTP LIMIT limits_smtp -| SMTP CIPHERS STRING { - conf->sc_tls_ciphers = $3; -} -| SMTP MAX_MESSAGE_SIZE size { - conf->sc_maxsize = $3; -} -| SMTP SUB_ADDR_DELIM STRING { - if (strlen($3) != 1) { - yyerror("subaddressing-delimiter must be one character"); - free($3); - YYERROR; - } - if (isspace((unsigned char)*$3) || !isprint((unsigned char)*$3) || *$3 == '@') { - yyerror("sub-addr-delim uses invalid character"); - free($3); - YYERROR; - } - conf->sc_subaddressing_delim = $3; -} -; - -srs: -SRS KEY STRING { - conf->sc_srs_key = $3; -} -| SRS KEY BACKUP STRING { - conf->sc_srs_key_backup = $4; -} -| SRS TTL STRING { - conf->sc_srs_ttl = delaytonum($3); - if (conf->sc_srs_ttl == -1) { - yyerror("ttl delay \"%s\" is invalid", $3); - free($3); - YYERROR; - } - - conf->sc_srs_ttl /= 86400; - if (conf->sc_srs_ttl == 0) { - yyerror("ttl delay \"%s\" is too short", $3); - free($3); - YYERROR; - } - free($3); -} -; - - -dispatcher_local_option: -USER STRING { - if (dispatcher->u.local.is_mbox) { - yyerror("user may not be specified for this dispatcher"); - YYERROR; - } - - if (dispatcher->u.local.forward_only) { - yyerror("user may not be specified for forward-only"); - YYERROR; - } - - if (dispatcher->u.local.expand_only) { - yyerror("user may not be specified for expand-only"); - YYERROR; - } - - if (dispatcher->u.local.user) { - yyerror("user already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.local.user = $2; -} -| ALIAS tables { - struct table *t = $2; - - if (dispatcher->u.local.table_alias) { - yyerror("alias mapping already specified for this dispatcher"); - YYERROR; - } - - if (dispatcher->u.local.table_virtual) { - yyerror("virtual mapping already specified for this dispatcher"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { - yyerror("table \"%s\" may not be used for alias lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.local.table_alias = strdup(t->t_name); -} -| VIRTUAL tables { - struct table *t = $2; - - if (dispatcher->u.local.table_virtual) { - yyerror("virtual mapping already specified for this dispatcher"); - YYERROR; - } - - if (dispatcher->u.local.table_alias) { - yyerror("alias mapping already specified for this dispatcher"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { - yyerror("table \"%s\" may not be used for virtual lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.local.table_virtual = strdup(t->t_name); -} -| USERBASE tables { - struct table *t = $2; - - if (dispatcher->u.local.table_userbase) { - yyerror("userbase mapping already specified for this dispatcher"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) { - yyerror("table \"%s\" may not be used for userbase lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.local.table_userbase = strdup(t->t_name); -} -| WRAPPER STRING { - if (! dict_get(conf->sc_mda_wrappers, $2)) { - yyerror("no mda wrapper with that name: %s", $2); - YYERROR; - } - dispatcher->u.local.mda_wrapper = $2; -} -; - -dispatcher_local_options: -dispatcher_local_option dispatcher_local_options -| /* empty */ -; - -dispatcher_local: -MBOX { - dispatcher->u.local.is_mbox = 1; - asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.local -f %%{mbox.from} -- %%{user.username}"); -} dispatcher_local_options -| MAILDIR { - asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir"); -} dispatcher_local_options -| MAILDIR JUNK { - asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir -j"); -} dispatcher_local_options -| MAILDIR STRING { - if (strncmp($2, "~/", 2) == 0) - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.maildir \"%%{user.directory}/%s\"", $2+2); - else - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.maildir \"%s\"", $2); -} dispatcher_local_options -| MAILDIR STRING JUNK { - if (strncmp($2, "~/", 2) == 0) - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.maildir -j \"%%{user.directory}/%s\"", $2+2); - else - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.maildir -j \"%s\"", $2); -} dispatcher_local_options -| LMTP STRING { - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.lmtp -d \"%s\" -u", $2); -} dispatcher_local_options -| LMTP STRING RCPT_TO { - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.lmtp -d \"%s\" -r", $2); -} dispatcher_local_options -| MDA STRING { - asprintf(&dispatcher->u.local.command, - PATH_LIBEXEC"/mail.mda \"%s\"", $2); -} dispatcher_local_options -| FORWARD_ONLY { - dispatcher->u.local.forward_only = 1; -} dispatcher_local_options -| EXPAND_ONLY { - dispatcher->u.local.expand_only = 1; -} dispatcher_local_options - -; - -dispatcher_remote_option: -HELO STRING { - if (dispatcher->u.remote.helo) { - yyerror("helo already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.helo = $2; -} -| HELO_SRC tables { - struct table *t = $2; - - if (dispatcher->u.remote.helo_source) { - yyerror("helo-source mapping already specified for this dispatcher"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { - yyerror("table \"%s\" may not be used for helo-source lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.remote.helo_source = strdup(t->t_name); -} -| PKI STRING { - if (dispatcher->u.remote.pki) { - yyerror("pki already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.pki = $2; -} -| CA STRING { - if (dispatcher->u.remote.ca) { - yyerror("ca already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.ca = $2; -} -| SRC tables { - struct table *t = $2; - - if (dispatcher->u.remote.source) { - yyerror("source mapping already specified for this dispatcher"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) { - yyerror("table \"%s\" may not be used for source lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.remote.source = strdup(t->t_name); -} -| MAIL_FROM STRING { - if (dispatcher->u.remote.mail_from) { - yyerror("mail-from already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.mail_from = $2; -} -| BACKUP MX STRING { - if (dispatcher->u.remote.backup) { - yyerror("backup already specified for this dispatcher"); - YYERROR; - } - if (dispatcher->u.remote.smarthost) { - yyerror("backup and host are mutually exclusive"); - YYERROR; - } - - dispatcher->u.remote.backup = 1; - dispatcher->u.remote.backupmx = $3; -} -| BACKUP { - if (dispatcher->u.remote.backup) { - yyerror("backup already specified for this dispatcher"); - YYERROR; - } - if (dispatcher->u.remote.smarthost) { - yyerror("backup and host are mutually exclusive"); - YYERROR; - } - - dispatcher->u.remote.backup = 1; -} -| HOST tables { - struct table *t = $2; - - if (dispatcher->u.remote.smarthost) { - yyerror("host mapping already specified for this dispatcher"); - YYERROR; - } - if (dispatcher->u.remote.backup) { - yyerror("backup and host are mutually exclusive"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_RELAYHOST)) { - yyerror("table \"%s\" may not be used for host lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.remote.smarthost = strdup(t->t_name); -} -| DOMAIN tables { - struct table *t = $2; - - if (dispatcher->u.remote.smarthost) { - yyerror("host mapping already specified for this dispatcher"); - YYERROR; - } - if (dispatcher->u.remote.backup) { - yyerror("backup and domain are mutually exclusive"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_RELAYHOST)) { - yyerror("table \"%s\" may not be used for host lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.remote.smarthost = strdup(t->t_name); - dispatcher->u.remote.smarthost_domain = 1; -} -| TLS { - if (dispatcher->u.remote.tls_required == 1) { - yyerror("tls already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.tls_required = 1; -} -| TLS NO_VERIFY { - if (dispatcher->u.remote.tls_required == 1) { - yyerror("tls already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.tls_required = 1; - dispatcher->u.remote.tls_noverify = 1; -} -| AUTH tables { - struct table *t = $2; - - if (dispatcher->u.remote.smarthost == NULL) { - yyerror("auth may not be specified without host on a dispatcher"); - YYERROR; - } - - if (dispatcher->u.remote.auth) { - yyerror("auth mapping already specified for this dispatcher"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) { - yyerror("table \"%s\" may not be used for auth lookups", - t->t_name); - YYERROR; - } - - dispatcher->u.remote.auth = strdup(t->t_name); -} -| FILTER STRING { - struct filter_config *fc; - - if (dispatcher->u.remote.filtername) { - yyerror("filter already specified for this dispatcher"); - YYERROR; - } - - if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) { - yyerror("no filter exist with that name: %s", $2); - free($2); - YYERROR; - } - fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT; - dispatcher->u.remote.filtername = $2; -} -| FILTER { - char buffer[128]; - char *filtername; - - if (dispatcher->u.remote.filtername) { - yyerror("filter already specified for this dispatcher"); - YYERROR; - } - - do { - (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++); - } while (dict_check(conf->sc_filters_dict, buffer)); - - filtername = xstrdup(buffer); - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->filter_type = FILTER_TYPE_CHAIN; - filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT; - dict_init(&filter_config->chain_procs); - dispatcher->u.remote.filtername = filtername; -} '{' filter_list '}' { - dict_set(conf->sc_filters_dict, dispatcher->u.remote.filtername, filter_config); - filter_config = NULL; -} -| SRS { - if (conf->sc_srs_key == NULL) { - yyerror("an srs key is required for srs to be specified in an action"); - YYERROR; - } - if (dispatcher->u.remote.srs == 1) { - yyerror("srs already specified for this dispatcher"); - YYERROR; - } - - dispatcher->u.remote.srs = 1; -} -; - -dispatcher_remote_options: -dispatcher_remote_option dispatcher_remote_options -| /* empty */ -; - -dispatcher_remote : -RELAY dispatcher_remote_options -; - -dispatcher_type: -dispatcher_local { - dispatcher->type = DISPATCHER_LOCAL; -} -| dispatcher_remote { - dispatcher->type = DISPATCHER_REMOTE; -} -; - -dispatcher_option: -TTL STRING { - if (dispatcher->ttl) { - yyerror("ttl already specified for this dispatcher"); - YYERROR; - } - - dispatcher->ttl = delaytonum($2); - if (dispatcher->ttl == -1) { - yyerror("ttl delay \"%s\" is invalid", $2); - free($2); - YYERROR; - } - free($2); -} -; - -dispatcher_options: -dispatcher_option dispatcher_options -| /* empty */ -; - -dispatcher: -ACTION STRING { - if (dict_get(conf->sc_dispatchers, $2)) { - yyerror("dispatcher already declared with that name: %s", $2); - YYERROR; - } - dispatcher = xcalloc(1, sizeof *dispatcher); -} dispatcher_type dispatcher_options { - if (dispatcher->type == DISPATCHER_LOCAL) - if (dispatcher->u.local.table_userbase == NULL) - dispatcher->u.local.table_userbase = "<getpwnam>"; - dict_set(conf->sc_dispatchers, $2, dispatcher); - dispatcher = NULL; -} -; - -match_option: -negation TAG tables { - struct table *t = $3; - - if (rule->flag_tag) { - yyerror("tag already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING)) { - yyerror("table \"%s\" may not be used for tag lookups", - t->t_name); - YYERROR; - } - - rule->flag_tag = $1 ? -1 : 1; - rule->table_tag = strdup(t->t_name); -} -| -negation TAG REGEX tables { - struct table *t = $4; - - if (rule->flag_tag) { - yyerror("tag already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for tag lookups", - t->t_name); - YYERROR; - } - - rule->flag_tag = $1 ? -1 : 1; - rule->flag_tag_regex = 1; - rule->table_tag = strdup(t->t_name); -} - -| negation HELO tables { - struct table *t = $3; - - if (rule->flag_smtp_helo) { - yyerror("helo already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { - yyerror("table \"%s\" may not be used for helo lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_helo = $1 ? -1 : 1; - rule->table_smtp_helo = strdup(t->t_name); -} -| negation HELO REGEX tables { - struct table *t = $4; - - if (rule->flag_smtp_helo) { - yyerror("helo already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for helo lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_helo = $1 ? -1 : 1; - rule->flag_smtp_helo_regex = 1; - rule->table_smtp_helo = strdup(t->t_name); -} -| negation TLS { - if (rule->flag_smtp_starttls) { - yyerror("tls already specified for this rule"); - YYERROR; - } - rule->flag_smtp_starttls = $1 ? -1 : 1; -} -| negation AUTH { - if (rule->flag_smtp_auth) { - yyerror("auth already specified for this rule"); - YYERROR; - } - rule->flag_smtp_auth = $1 ? -1 : 1; -} -| negation AUTH tables { - struct table *t = $3; - - if (rule->flag_smtp_auth) { - yyerror("auth already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) { - yyerror("table \"%s\" may not be used for auth lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_auth = $1 ? -1 : 1; - rule->table_smtp_auth = strdup(t->t_name); -} -| negation AUTH REGEX tables { - struct table *t = $4; - - if (rule->flag_smtp_auth) { - yyerror("auth already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for auth lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_auth = $1 ? -1 : 1; - rule->flag_smtp_auth_regex = 1; - rule->table_smtp_auth = strdup(t->t_name); -} -| negation MAIL_FROM tables { - struct table *t = $3; - - if (rule->flag_smtp_mail_from) { - yyerror("mail-from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("table \"%s\" may not be used for mail-from lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_mail_from = $1 ? -1 : 1; - rule->table_smtp_mail_from = strdup(t->t_name); -} -| negation MAIL_FROM REGEX tables { - struct table *t = $4; - - if (rule->flag_smtp_mail_from) { - yyerror("mail-from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for mail-from lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_mail_from = $1 ? -1 : 1; - rule->flag_smtp_mail_from_regex = 1; - rule->table_smtp_mail_from = strdup(t->t_name); -} -| negation RCPT_TO tables { - struct table *t = $3; - - if (rule->flag_smtp_rcpt_to) { - yyerror("rcpt-to already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("table \"%s\" may not be used for rcpt-to lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_rcpt_to = $1 ? -1 : 1; - rule->table_smtp_rcpt_to = strdup(t->t_name); -} -| negation RCPT_TO REGEX tables { - struct table *t = $4; - - if (rule->flag_smtp_rcpt_to) { - yyerror("rcpt-to already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for rcpt-to lookups", - t->t_name); - YYERROR; - } - - rule->flag_smtp_rcpt_to = $1 ? -1 : 1; - rule->flag_smtp_rcpt_to_regex = 1; - rule->table_smtp_rcpt_to = strdup(t->t_name); -} - -| negation FROM SOCKET { - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - rule->flag_from = $1 ? -1 : 1; - rule->flag_from_socket = 1; -} -| negation FROM LOCAL { - struct table *t = table_find(conf, "<localhost>"); - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - rule->flag_from = $1 ? -1 : 1; - rule->table_from = strdup(t->t_name); -} -| negation FROM ANY { - struct table *t = table_find(conf, "<anyhost>"); - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - rule->flag_from = $1 ? -1 : 1; - rule->table_from = strdup(t->t_name); -} -| negation FROM SRC tables { - struct table *t = $4; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { - yyerror("table \"%s\" may not be used for from lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = $1 ? -1 : 1; - rule->table_from = strdup(t->t_name); -} -| negation FROM SRC REGEX tables { - struct table *t = $5; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for from lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = $1 ? -1 : 1; - rule->flag_from_regex = 1; - rule->table_from = strdup(t->t_name); -} -| negation FROM RDNS { - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - rule->flag_from = $1 ? -1 : 1; - rule->flag_from_rdns = 1; -} -| negation FROM RDNS tables { - struct table *t = $4; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { - yyerror("table \"%s\" may not be used for rdns lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = $1 ? -1 : 1; - rule->flag_from_rdns = 1; - rule->table_from = strdup(t->t_name); -} -| negation FROM RDNS REGEX tables { - struct table *t = $5; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { - yyerror("table \"%s\" may not be used for rdns lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = $1 ? -1 : 1; - rule->flag_from_regex = 1; - rule->flag_from_rdns = 1; - rule->table_from = strdup(t->t_name); -} - -| negation FROM AUTH { - struct table *anyhost = table_find(conf, "<anyhost>"); - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - rule->flag_from = 1; - rule->table_from = strdup(anyhost->t_name); - rule->flag_smtp_auth = $1 ? -1 : 1; -} -| negation FROM AUTH tables { - struct table *anyhost = table_find(conf, "<anyhost>"); - struct table *t = $4; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) { - yyerror("table \"%s\" may not be used for from lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = 1; - rule->table_from = strdup(anyhost->t_name); - rule->flag_smtp_auth = $1 ? -1 : 1; - rule->table_smtp_auth = strdup(t->t_name); -} -| negation FROM AUTH REGEX tables { - struct table *anyhost = table_find(conf, "<anyhost>"); - struct table *t = $5; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for from lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = 1; - rule->table_from = strdup(anyhost->t_name); - rule->flag_smtp_auth = $1 ? -1 : 1; - rule->flag_smtp_auth_regex = 1; - rule->table_smtp_auth = strdup(t->t_name); -} - -| negation FROM MAIL_FROM tables { - struct table *anyhost = table_find(conf, "<anyhost>"); - struct table *t = $4; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("table \"%s\" may not be used for from lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = 1; - rule->table_from = strdup(anyhost->t_name); - rule->flag_smtp_mail_from = $1 ? -1 : 1; - rule->table_smtp_mail_from = strdup(t->t_name); -} -| negation FROM MAIL_FROM REGEX tables { - struct table *anyhost = table_find(conf, "<anyhost>"); - struct table *t = $5; - - if (rule->flag_from) { - yyerror("from already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for from lookups", - t->t_name); - YYERROR; - } - - rule->flag_from = 1; - rule->table_from = strdup(anyhost->t_name); - rule->flag_smtp_mail_from = $1 ? -1 : 1; - rule->flag_smtp_mail_from_regex = 1; - rule->table_smtp_mail_from = strdup(t->t_name); -} - -| negation FOR LOCAL { - struct table *t = table_find(conf, "<localnames>"); - - if (rule->flag_for) { - yyerror("for already specified for this rule"); - YYERROR; - } - rule->flag_for = $1 ? -1 : 1; - rule->table_for = strdup(t->t_name); -} -| negation FOR ANY { - struct table *t = table_find(conf, "<anydestination>"); - - if (rule->flag_for) { - yyerror("for already specified for this rule"); - YYERROR; - } - rule->flag_for = $1 ? -1 : 1; - rule->table_for = strdup(t->t_name); -} -| negation FOR DOMAIN tables { - struct table *t = $4; - - if (rule->flag_for) { - yyerror("for already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { - yyerror("table \"%s\" may not be used for 'for' lookups", - t->t_name); - YYERROR; - } - - rule->flag_for = $1 ? -1 : 1; - rule->table_for = strdup(t->t_name); -} -| negation FOR DOMAIN REGEX tables { - struct table *t = $5; - - if (rule->flag_for) { - yyerror("for already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for 'for' lookups", - t->t_name); - YYERROR; - } - - rule->flag_for = $1 ? -1 : 1; - rule->flag_for_regex = 1; - rule->table_for = strdup(t->t_name); -} -| negation FOR RCPT_TO tables { - struct table *anyhost = table_find(conf, "<anydestination>"); - struct table *t = $4; - - if (rule->flag_for) { - yyerror("for already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("table \"%s\" may not be used for for lookups", - t->t_name); - YYERROR; - } - - rule->flag_for = 1; - rule->table_for = strdup(anyhost->t_name); - rule->flag_smtp_rcpt_to = $1 ? -1 : 1; - rule->table_smtp_rcpt_to = strdup(t->t_name); -} -| negation FOR RCPT_TO REGEX tables { - struct table *anyhost = table_find(conf, "<anydestination>"); - struct table *t = $5; - - if (rule->flag_for) { - yyerror("for already specified for this rule"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { - yyerror("table \"%s\" may not be used for for lookups", - t->t_name); - YYERROR; - } - - rule->flag_for = 1; - rule->table_for = strdup(anyhost->t_name); - rule->flag_smtp_rcpt_to = $1 ? -1 : 1; - rule->flag_smtp_rcpt_to_regex = 1; - rule->table_smtp_rcpt_to = strdup(t->t_name); -} -; - -match_options: -match_option match_options -| /* empty */ -; - -match_dispatcher: -STRING { - if (dict_get(conf->sc_dispatchers, $1) == NULL) { - yyerror("no such dispatcher: %s", $1); - YYERROR; - } - rule->dispatcher = $1; -} -; - -action: -REJECT { - rule->reject = 1; -} -| ACTION match_dispatcher -; - -match: -MATCH { - rule = xcalloc(1, sizeof *rule); -} match_options action { - if (!rule->flag_from) { - rule->table_from = strdup("<localhost>"); - rule->flag_from = 1; - } - if (!rule->flag_for) { - rule->table_for = strdup("<localnames>"); - rule->flag_for = 1; - } - TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); - rule = NULL; -} -; - -filter_action_builtin: -filter_action_builtin_nojunk -| JUNK { - filter_config->junk = 1; -} -| BYPASS { - filter_config->bypass = 1; -} -; - -filter_action_builtin_nojunk: -REJECT STRING { - filter_config->reject = $2; -} -| DISCONNECT STRING { - filter_config->disconnect = $2; -} -| REWRITE STRING { - filter_config->rewrite = $2; -} -| REPORT STRING { - filter_config->report = $2; -} -; - -filter_phase_check_fcrdns: -negation FCRDNS { - filter_config->not_fcrdns = $1 ? -1 : 1; - filter_config->fcrdns = 1; -} -; - -filter_phase_check_rdns: -negation RDNS { - filter_config->not_rdns = $1 ? -1 : 1; - filter_config->rdns = 1; -} -; - -filter_phase_check_rdns_table: -negation RDNS tables { - filter_config->not_rdns_table = $1 ? -1 : 1; - filter_config->rdns_table = $3; -} -; -filter_phase_check_rdns_regex: -negation RDNS REGEX tables { - filter_config->not_rdns_regex = $1 ? -1 : 1; - filter_config->rdns_regex = $4; -} -; - -filter_phase_check_src_table: -negation SRC tables { - filter_config->not_src_table = $1 ? -1 : 1; - filter_config->src_table = $3; -} -; -filter_phase_check_src_regex: -negation SRC REGEX tables { - filter_config->not_src_regex = $1 ? -1 : 1; - filter_config->src_regex = $4; -} -; - -filter_phase_check_helo_table: -negation HELO tables { - filter_config->not_helo_table = $1 ? -1 : 1; - filter_config->helo_table = $3; -} -; -filter_phase_check_helo_regex: -negation HELO REGEX tables { - filter_config->not_helo_regex = $1 ? -1 : 1; - filter_config->helo_regex = $4; -} -; - -filter_phase_check_auth: -negation AUTH { - filter_config->not_auth = $1 ? -1 : 1; - filter_config->auth = 1; -} -; -filter_phase_check_auth_table: -negation AUTH tables { - filter_config->not_auth_table = $1 ? -1 : 1; - filter_config->auth_table = $3; -} -; -filter_phase_check_auth_regex: -negation AUTH REGEX tables { - filter_config->not_auth_regex = $1 ? -1 : 1; - filter_config->auth_regex = $4; -} -; - -filter_phase_check_mail_from_table: -negation MAIL_FROM tables { - filter_config->not_mail_from_table = $1 ? -1 : 1; - filter_config->mail_from_table = $3; -} -; -filter_phase_check_mail_from_regex: -negation MAIL_FROM REGEX tables { - filter_config->not_mail_from_regex = $1 ? -1 : 1; - filter_config->mail_from_regex = $4; -} -; - -filter_phase_check_rcpt_to_table: -negation RCPT_TO tables { - filter_config->not_rcpt_to_table = $1 ? -1 : 1; - filter_config->rcpt_to_table = $3; -} -; -filter_phase_check_rcpt_to_regex: -negation RCPT_TO REGEX tables { - filter_config->not_rcpt_to_regex = $1 ? -1 : 1; - filter_config->rcpt_to_regex = $4; -} -; - -filter_phase_global_options: -filter_phase_check_fcrdns | -filter_phase_check_rdns | -filter_phase_check_rdns_regex | -filter_phase_check_rdns_table | -filter_phase_check_src_regex | -filter_phase_check_src_table; - -filter_phase_connect_options: -filter_phase_global_options; - -filter_phase_helo_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_global_options; - -filter_phase_auth_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_check_auth | -filter_phase_check_auth_table | -filter_phase_check_auth_regex | -filter_phase_global_options; - -filter_phase_mail_from_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_check_auth | -filter_phase_check_auth_table | -filter_phase_check_auth_regex | -filter_phase_check_mail_from_table | -filter_phase_check_mail_from_regex | -filter_phase_global_options; - -filter_phase_rcpt_to_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_check_auth | -filter_phase_check_auth_table | -filter_phase_check_auth_regex | -filter_phase_check_mail_from_table | -filter_phase_check_mail_from_regex | -filter_phase_check_rcpt_to_table | -filter_phase_check_rcpt_to_regex | -filter_phase_global_options; - -filter_phase_data_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_check_auth | -filter_phase_check_auth_table | -filter_phase_check_auth_regex | -filter_phase_check_mail_from_table | -filter_phase_check_mail_from_regex | -filter_phase_global_options; - -/* -filter_phase_quit_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_global_options; - -filter_phase_rset_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_global_options; - -filter_phase_noop_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_global_options; -*/ - -filter_phase_commit_options: -filter_phase_check_helo_table | -filter_phase_check_helo_regex | -filter_phase_check_auth | -filter_phase_check_auth_table | -filter_phase_check_auth_regex | -filter_phase_check_mail_from_table | -filter_phase_check_mail_from_regex | -filter_phase_global_options; - - -filter_phase_connect: -CONNECT { - filter_config->phase = FILTER_CONNECT; -} MATCH filter_phase_connect_options filter_action_builtin -; - - -filter_phase_helo: -HELO { - filter_config->phase = FILTER_HELO; -} MATCH filter_phase_helo_options filter_action_builtin -; - -filter_phase_ehlo: -EHLO { - filter_config->phase = FILTER_EHLO; -} MATCH filter_phase_helo_options filter_action_builtin -; - -filter_phase_auth: -AUTH { -} MATCH filter_phase_auth_options filter_action_builtin -; - -filter_phase_mail_from: -MAIL_FROM { - filter_config->phase = FILTER_MAIL_FROM; -} MATCH filter_phase_mail_from_options filter_action_builtin -; - -filter_phase_rcpt_to: -RCPT_TO { - filter_config->phase = FILTER_RCPT_TO; -} MATCH filter_phase_rcpt_to_options filter_action_builtin -; - -filter_phase_data: -DATA { - filter_config->phase = FILTER_DATA; -} MATCH filter_phase_data_options filter_action_builtin -; - -/* -filter_phase_data_line: -DATA_LINE { - filter_config->phase = FILTER_DATA_LINE; -} MATCH filter_action_builtin -; - -filter_phase_quit: -QUIT { - filter_config->phase = FILTER_QUIT; -} filter_phase_quit_options filter_action_builtin -; - -filter_phase_rset: -RSET { - filter_config->phase = FILTER_RSET; -} MATCH filter_phase_rset_options filter_action_builtin -; - -filter_phase_noop: -NOOP { - filter_config->phase = FILTER_NOOP; -} MATCH filter_phase_noop_options filter_action_builtin -; -*/ - -filter_phase_commit: -COMMIT { - filter_config->phase = FILTER_COMMIT; -} MATCH filter_phase_commit_options filter_action_builtin_nojunk -; - - - -filter_phase: -filter_phase_connect -| filter_phase_helo -| filter_phase_ehlo -| filter_phase_auth -| filter_phase_mail_from -| filter_phase_rcpt_to -| filter_phase_data -/*| filter_phase_data_line*/ -/*| filter_phase_quit*/ -/*| filter_phase_noop*/ -/*| filter_phase_rset*/ -| filter_phase_commit -; - - -filterel: -STRING { - struct filter_config *fr; - struct filter_proc *fp; - size_t i; - - if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) { - yyerror("no filter exist with that name: %s", $1); - free($1); - YYERROR; - } - if (fr->filter_type == FILTER_TYPE_CHAIN) { - yyerror("no filter chain allowed within a filter chain: %s", $1); - free($1); - YYERROR; - } - - for (i = 0; i < filter_config->chain_size; i++) { - if (strcmp(filter_config->chain[i], $1) == 0) { - yyerror("no filter allowed twice within a filter chain: %s", $1); - free($1); - YYERROR; - } - } - - if (fr->proc) { - if ((fp = dict_get(&filter_config->chain_procs, fr->proc))) { - yyerror("no proc allowed twice within a filter chain: %s", fr->proc); - free($1); - YYERROR; - } - dict_set(&filter_config->chain_procs, fr->proc, NULL); - } - - fr->filter_subsystem |= filter_config->filter_subsystem; - filter_config->chain_size += 1; - filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *)); - if (filter_config->chain == NULL) - err(1, NULL); - filter_config->chain[filter_config->chain_size - 1] = $1; -} -; - -filter_list: -filterel -| filterel comma filter_list -; - -filter: -FILTER STRING PROC STRING { - struct filter_proc *fp; - - if (dict_get(conf->sc_filters_dict, $2)) { - yyerror("filter already exists with that name: %s", $2); - free($2); - free($4); - YYERROR; - } - if ((fp = dict_get(conf->sc_filter_processes_dict, $4)) == NULL) { - yyerror("no processor exist with that name: %s", $4); - free($4); - YYERROR; - } - - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->filter_type = FILTER_TYPE_PROC; - filter_config->name = $2; - filter_config->proc = $4; - dict_set(conf->sc_filters_dict, $2, filter_config); - filter_config = NULL; -} -| -FILTER STRING PROC_EXEC STRING { - if (dict_get(conf->sc_filters_dict, $2)) { - yyerror("filter already exists with that name: %s", $2); - free($2); - free($4); - YYERROR; - } - - processor = xcalloc(1, sizeof *processor); - processor->command = $4; - - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->filter_type = FILTER_TYPE_PROC; - filter_config->name = $2; - filter_config->proc = xstrdup($2); - dict_set(conf->sc_filters_dict, $2, filter_config); -} proc_params { - dict_set(conf->sc_filter_processes_dict, filter_config->proc, processor); - processor = NULL; - filter_config = NULL; -} -| -FILTER STRING PHASE { - if (dict_get(conf->sc_filters_dict, $2)) { - yyerror("filter already exists with that name: %s", $2); - free($2); - YYERROR; - } - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->name = $2; - filter_config->filter_type = FILTER_TYPE_BUILTIN; - dict_set(conf->sc_filters_dict, $2, filter_config); -} filter_phase { - filter_config = NULL; -} -| -FILTER STRING CHAIN { - if (dict_get(conf->sc_filters_dict, $2)) { - yyerror("filter already exists with that name: %s", $2); - free($2); - YYERROR; - } - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->filter_type = FILTER_TYPE_CHAIN; - dict_init(&filter_config->chain_procs); -} '{' filter_list '}' { - dict_set(conf->sc_filters_dict, $2, filter_config); - filter_config = NULL; -} -; - -size : NUMBER { - if ($1 < 0) { - yyerror("invalid size: %" PRId64, $1); - YYERROR; - } - $$ = $1; - } - | STRING { - long long result; - - if (scan_scaled($1, &result) == -1 || result < 0) { - yyerror("invalid size: %s", $1); - free($1); - YYERROR; - } - free($1); - $$ = result; - } - ; - -bouncedelay : STRING { - time_t d; - int i; - - d = delaytonum($1); - if (d < 0) { - yyerror("invalid bounce delay: %s", $1); - free($1); - YYERROR; - } - free($1); - for (i = 0; i < MAX_BOUNCE_WARN; i++) { - if (conf->sc_bounce_warn[i] != 0) - continue; - conf->sc_bounce_warn[i] = d; - break; - } - } - ; - -bouncedelays : bouncedelays ',' bouncedelay - | bouncedelay - ; - -opt_limit_mda : STRING NUMBER { - if (!strcmp($1, "max-session")) { - conf->sc_mda_max_session = $2; - } - else if (!strcmp($1, "max-session-per-user")) { - conf->sc_mda_max_user_session = $2; - } - else if (!strcmp($1, "task-lowat")) { - conf->sc_mda_task_lowat = $2; - } - else if (!strcmp($1, "task-hiwat")) { - conf->sc_mda_task_hiwat = $2; - } - else if (!strcmp($1, "task-release")) { - conf->sc_mda_task_release = $2; - } - else { - yyerror("invalid scheduler limit keyword: %s", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -limits_smtp : opt_limit_smtp limits_smtp - | /* empty */ - ; - -opt_limit_smtp : STRING NUMBER { - if (!strcmp($1, "max-rcpt")) { - conf->sc_session_max_rcpt = $2; - } - else if (!strcmp($1, "max-mails")) { - conf->sc_session_max_mails = $2; - } - else { - yyerror("invalid session limit keyword: %s", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -limits_mda : opt_limit_mda limits_mda - | /* empty */ - ; - -opt_limit_mta : INET4 { - limits->family = AF_INET; - } - | INET6 { - limits->family = AF_INET6; - } - | STRING NUMBER { - if (!limit_mta_set(limits, $1, $2)) { - yyerror("invalid mta limit keyword: %s", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -limits_mta : opt_limit_mta limits_mta - | /* empty */ - ; - -opt_limit_scheduler : STRING NUMBER { - if (!strcmp($1, "max-inflight")) { - conf->sc_scheduler_max_inflight = $2; - } - else if (!strcmp($1, "max-evp-batch-size")) { - conf->sc_scheduler_max_evp_batch_size = $2; - } - else if (!strcmp($1, "max-msg-batch-size")) { - conf->sc_scheduler_max_msg_batch_size = $2; - } - else if (!strcmp($1, "max-schedule")) { - conf->sc_scheduler_max_schedule = $2; - } - else { - yyerror("invalid scheduler limit keyword: %s", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -limits_scheduler: opt_limit_scheduler limits_scheduler - | /* empty */ - ; - - -opt_sock_listen : FILTER STRING { - struct filter_config *fc; - - if (listen_opts.options & LO_FILTER) { - yyerror("filter already specified"); - free($2); - YYERROR; - } - if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) { - yyerror("no filter exist with that name: %s", $2); - free($2); - YYERROR; - } - fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; - listen_opts.options |= LO_FILTER; - listen_opts.filtername = $2; - } - | FILTER { - char buffer[128]; - - if (listen_opts.options & LO_FILTER) { - yyerror("filter already specified"); - YYERROR; - } - - do { - (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++); - } while (dict_check(conf->sc_filters_dict, buffer)); - - listen_opts.options |= LO_FILTER; - listen_opts.filtername = xstrdup(buffer); - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->filter_type = FILTER_TYPE_CHAIN; - filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; - dict_init(&filter_config->chain_procs); - } '{' filter_list '}' { - dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config); - filter_config = NULL; - } - | MASK_SRC { - if (config_lo_mask_source(&listen_opts)) { - YYERROR; - } - } - | TAG STRING { - if (listen_opts.options & LO_TAG) { - yyerror("tag already specified"); - YYERROR; - } - listen_opts.options |= LO_TAG; - - if (strlen($2) >= SMTPD_TAG_SIZE) { - yyerror("tag name too long"); - free($2); - YYERROR; - } - listen_opts.tag = $2; - } - ; - -opt_if_listen : INET4 { - if (listen_opts.options & LO_FAMILY) { - yyerror("address family already specified"); - YYERROR; - } - listen_opts.options |= LO_FAMILY; - listen_opts.family = AF_INET; - } - | INET6 { - if (listen_opts.options & LO_FAMILY) { - yyerror("address family already specified"); - YYERROR; - } - listen_opts.options |= LO_FAMILY; - listen_opts.family = AF_INET6; - } - | PORT STRING { - struct servent *servent; - - if (listen_opts.options & LO_PORT) { - yyerror("port already specified"); - YYERROR; - } - listen_opts.options |= LO_PORT; - - servent = getservbyname($2, "tcp"); - if (servent == NULL) { - yyerror("invalid port: %s", $2); - free($2); - YYERROR; - } - free($2); - listen_opts.port = ntohs(servent->s_port); - } - | PORT SMTP { - struct servent *servent; - - if (listen_opts.options & LO_PORT) { - yyerror("port already specified"); - YYERROR; - } - listen_opts.options |= LO_PORT; - - servent = getservbyname("smtp", "tcp"); - if (servent == NULL) { - yyerror("invalid port: smtp"); - YYERROR; - } - listen_opts.port = ntohs(servent->s_port); - } - | PORT SMTPS { - struct servent *servent; - - if (listen_opts.options & LO_PORT) { - yyerror("port already specified"); - YYERROR; - } - listen_opts.options |= LO_PORT; - - servent = getservbyname("smtps", "tcp"); - if (servent == NULL) { - yyerror("invalid port: smtps"); - YYERROR; - } - listen_opts.port = ntohs(servent->s_port); - } - | PORT NUMBER { - if (listen_opts.options & LO_PORT) { - yyerror("port already specified"); - YYERROR; - } - listen_opts.options |= LO_PORT; - - if ($2 <= 0 || $2 > (int)USHRT_MAX) { - yyerror("invalid port: %" PRId64, $2); - YYERROR; - } - listen_opts.port = $2; - } - | FILTER STRING { - struct filter_config *fc; - - if (listen_opts.options & LO_FILTER) { - yyerror("filter already specified"); - YYERROR; - } - if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) { - yyerror("no filter exist with that name: %s", $2); - free($2); - YYERROR; - } - fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; - listen_opts.options |= LO_FILTER; - listen_opts.filtername = $2; - } - | FILTER { - char buffer[128]; - - if (listen_opts.options & LO_FILTER) { - yyerror("filter already specified"); - YYERROR; - } - - do { - (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++); - } while (dict_check(conf->sc_filters_dict, buffer)); - - listen_opts.options |= LO_FILTER; - listen_opts.filtername = xstrdup(buffer); - filter_config = xcalloc(1, sizeof *filter_config); - filter_config->filter_type = FILTER_TYPE_CHAIN; - filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; - dict_init(&filter_config->chain_procs); - } '{' filter_list '}' { - dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config); - filter_config = NULL; - } - | SMTPS { - if (listen_opts.options & LO_SSL) { - yyerror("TLS mode already specified"); - YYERROR; - } - listen_opts.options |= LO_SSL; - listen_opts.ssl = F_SMTPS; - } - | SMTPS VERIFY { - if (listen_opts.options & LO_SSL) { - yyerror("TLS mode already specified"); - YYERROR; - } - listen_opts.options |= LO_SSL; - listen_opts.ssl = F_SMTPS|F_TLS_VERIFY; - } - | TLS { - if (listen_opts.options & LO_SSL) { - yyerror("TLS mode already specified"); - YYERROR; - } - listen_opts.options |= LO_SSL; - listen_opts.ssl = F_STARTTLS; - } - | TLS_REQUIRE { - if (listen_opts.options & LO_SSL) { - yyerror("TLS mode already specified"); - YYERROR; - } - listen_opts.options |= LO_SSL; - listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE; - } - | TLS_REQUIRE VERIFY { - if (listen_opts.options & LO_SSL) { - yyerror("TLS mode already specified"); - YYERROR; - } - listen_opts.options |= LO_SSL; - listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY; - } - | PKI STRING { - if (listen_opts.options & LO_PKI) { - yyerror("pki already specified"); - YYERROR; - } - listen_opts.options |= LO_PKI; - listen_opts.pki = $2; - } - | CA STRING { - if (listen_opts.options & LO_CA) { - yyerror("ca already specified"); - YYERROR; - } - listen_opts.options |= LO_CA; - listen_opts.ca = $2; - } - | AUTH { - if (listen_opts.options & LO_AUTH) { - yyerror("auth already specified"); - YYERROR; - } - listen_opts.options |= LO_AUTH; - listen_opts.auth = F_AUTH|F_AUTH_REQUIRE; - } - | AUTH_OPTIONAL { - if (listen_opts.options & LO_AUTH) { - yyerror("auth already specified"); - YYERROR; - } - listen_opts.options |= LO_AUTH; - listen_opts.auth = F_AUTH; - } - | AUTH tables { - if (listen_opts.options & LO_AUTH) { - yyerror("auth already specified"); - YYERROR; - } - listen_opts.options |= LO_AUTH; - listen_opts.authtable = $2; - listen_opts.auth = F_AUTH|F_AUTH_REQUIRE; - } - | AUTH_OPTIONAL tables { - if (listen_opts.options & LO_AUTH) { - yyerror("auth already specified"); - YYERROR; - } - listen_opts.options |= LO_AUTH; - listen_opts.authtable = $2; - listen_opts.auth = F_AUTH; - } - | TAG STRING { - if (listen_opts.options & LO_TAG) { - yyerror("tag already specified"); - YYERROR; - } - listen_opts.options |= LO_TAG; - - if (strlen($2) >= SMTPD_TAG_SIZE) { - yyerror("tag name too long"); - free($2); - YYERROR; - } - listen_opts.tag = $2; - } - | HOSTNAME STRING { - if (listen_opts.options & LO_HOSTNAME) { - yyerror("hostname already specified"); - YYERROR; - } - listen_opts.options |= LO_HOSTNAME; - - listen_opts.hostname = $2; - } - | HOSTNAMES tables { - struct table *t = $2; - - if (listen_opts.options & LO_HOSTNAMES) { - yyerror("hostnames already specified"); - YYERROR; - } - listen_opts.options |= LO_HOSTNAMES; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { - yyerror("invalid use of table \"%s\" as " - "HOSTNAMES parameter", t->t_name); - YYERROR; - } - listen_opts.hostnametable = t; - } - | MASK_SRC { - if (config_lo_mask_source(&listen_opts)) { - YYERROR; - } - } - | RECEIVEDAUTH { - if (listen_opts.options & LO_RECEIVEDAUTH) { - yyerror("received-auth already specified"); - YYERROR; - } - listen_opts.options |= LO_RECEIVEDAUTH; - listen_opts.flags |= F_RECEIVEDAUTH; - } - | NO_DSN { - if (listen_opts.options & LO_NODSN) { - yyerror("no-dsn already specified"); - YYERROR; - } - listen_opts.options |= LO_NODSN; - listen_opts.flags &= ~F_EXT_DSN; - } - | PROXY_V2 { - if (listen_opts.options & LO_PROXY) { - yyerror("proxy-v2 already specified"); - YYERROR; - } - listen_opts.options |= LO_PROXY; - listen_opts.flags |= F_PROXY; - } - | SENDERS tables { - struct table *t = $2; - - if (listen_opts.options & LO_SENDERS) { - yyerror("senders already specified"); - YYERROR; - } - listen_opts.options |= LO_SENDERS; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_MAILADDRMAP)) { - yyerror("invalid use of table \"%s\" as " - "SENDERS parameter", t->t_name); - YYERROR; - } - listen_opts.sendertable = t; - } - | SENDERS tables MASQUERADE { - struct table *t = $2; - - if (listen_opts.options & LO_SENDERS) { - yyerror("senders already specified"); - YYERROR; - } - listen_opts.options |= LO_SENDERS|LO_MASQUERADE; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_MAILADDRMAP)) { - yyerror("invalid use of table \"%s\" as " - "SENDERS parameter", t->t_name); - YYERROR; - } - listen_opts.sendertable = t; - } - ; - -listener_type : socket_listener - | if_listener - ; - -socket_listener : SOCKET sock_listen { - if (conf->sc_sock_listener) { - yyerror("socket listener already configured"); - YYERROR; - } - create_sock_listener(&listen_opts); - } - ; - -if_listener : STRING if_listen { - listen_opts.ifx = $1; - create_if_listener(&listen_opts); - } - ; - -sock_listen : opt_sock_listen sock_listen - | /* empty */ - ; - -if_listen : opt_if_listen if_listen - | /* empty */ - ; - - -listen : LISTEN { - memset(&listen_opts, 0, sizeof listen_opts); - listen_opts.family = AF_UNSPEC; - listen_opts.flags |= F_EXT_DSN; - } ON listener_type - ; - -table : TABLE STRING STRING { - char *p, *backend, *config; - - p = $3; - if (*p == '/') { - backend = "static"; - config = $3; - } - else { - backend = $3; - config = NULL; - for (p = $3; *p && *p != ':'; p++) - ; - if (*p == ':') { - *p = '\0'; - backend = $3; - config = p+1; - } - } - if (config != NULL && *config != '/') { - yyerror("invalid backend parameter for table: %s", - $2); - free($2); - free($3); - YYERROR; - } - table = table_create(conf, backend, $2, config); - if (!table_config(table)) { - yyerror("invalid configuration file %s for table %s", - config, table->t_name); - free($2); - free($3); - YYERROR; - } - table = NULL; - free($2); - free($3); - } - | TABLE STRING { - table = table_create(conf, "static", $2, NULL); - free($2); - } '{' tableval_list '}' { - table = NULL; - } - ; - -tablenew : STRING { - struct table *t; - - t = table_create(conf, "static", NULL, NULL); - table_add(t, $1, NULL); - free($1); - $$ = t; - } - | '{' { - table = table_create(conf, "static", NULL, NULL); - } tableval_list '}' { - $$ = table; - table = NULL; - } - ; - -tableref : '<' STRING '>' { - struct table *t; - - if ((t = table_find(conf, $2)) == NULL) { - yyerror("no such table: %s", $2); - free($2); - YYERROR; - } - free($2); - $$ = t; - } - ; - -tables : tablenew { $$ = $1; } - | tableref { $$ = $1; } - ; - - -%% - -struct keywords { - const char *k_name; - int k_val; -}; - -int -yyerror(const char *fmt, ...) -{ - va_list ap; - char *msg; - - file->errors++; - va_start(ap, fmt); - if (vasprintf(&msg, fmt, ap) == -1) - fatalx("yyerror vasprintf"); - va_end(ap); - logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); - free(msg); - return (0); -} - -int -kw_cmp(const void *k, const void *e) -{ - return (strcmp(k, ((const struct keywords *)e)->k_name)); -} - -int -lookup(char *s) -{ - /* this has to be sorted always */ - static const struct keywords keywords[] = { - { "action", ACTION }, - { "alias", ALIAS }, - { "any", ANY }, - { "auth", AUTH }, - { "auth-optional", AUTH_OPTIONAL }, - { "backup", BACKUP }, - { "bounce", BOUNCE }, - { "bypass", BYPASS }, - { "ca", CA }, - { "cert", CERT }, - { "chain", CHAIN }, - { "chroot", CHROOT }, - { "ciphers", CIPHERS }, - { "commit", COMMIT }, - { "compression", COMPRESSION }, - { "connect", CONNECT }, - { "data", DATA }, - { "data-line", DATA_LINE }, - { "dhe", DHE }, - { "disconnect", DISCONNECT }, - { "domain", DOMAIN }, - { "ehlo", EHLO }, - { "encryption", ENCRYPTION }, - { "expand-only", EXPAND_ONLY }, - { "fcrdns", FCRDNS }, - { "filter", FILTER }, - { "for", FOR }, - { "forward-only", FORWARD_ONLY }, - { "from", FROM }, - { "group", GROUP }, - { "helo", HELO }, - { "helo-src", HELO_SRC }, - { "host", HOST }, - { "hostname", HOSTNAME }, - { "hostnames", HOSTNAMES }, - { "include", INCLUDE }, - { "inet4", INET4 }, - { "inet6", INET6 }, - { "junk", JUNK }, - { "key", KEY }, - { "limit", LIMIT }, - { "listen", LISTEN }, - { "lmtp", LMTP }, - { "local", LOCAL }, - { "mail-from", MAIL_FROM }, - { "maildir", MAILDIR }, - { "mask-src", MASK_SRC }, - { "masquerade", MASQUERADE }, - { "match", MATCH }, - { "max-deferred", MAX_DEFERRED }, - { "max-message-size", MAX_MESSAGE_SIZE }, - { "mbox", MBOX }, - { "mda", MDA }, - { "mta", MTA }, - { "mx", MX }, - { "no-dsn", NO_DSN }, - { "no-verify", NO_VERIFY }, - { "noop", NOOP }, - { "on", ON }, - { "phase", PHASE }, - { "pki", PKI }, - { "port", PORT }, - { "proc", PROC }, - { "proc-exec", PROC_EXEC }, - { "proxy-v2", PROXY_V2 }, - { "queue", QUEUE }, - { "quit", QUIT }, - { "rcpt-to", RCPT_TO }, - { "rdns", RDNS }, - { "received-auth", RECEIVEDAUTH }, - { "recipient", RECIPIENT }, - { "regex", REGEX }, - { "reject", REJECT }, - { "relay", RELAY }, - { "report", REPORT }, - { "rewrite", REWRITE }, - { "rset", RSET }, - { "scheduler", SCHEDULER }, - { "senders", SENDERS }, - { "smtp", SMTP }, - { "smtp-in", SMTP_IN }, - { "smtp-out", SMTP_OUT }, - { "smtps", SMTPS }, - { "socket", SOCKET }, - { "src", SRC }, - { "srs", SRS }, - { "sub-addr-delim", SUB_ADDR_DELIM }, - { "table", TABLE }, - { "tag", TAG }, - { "tagged", TAGGED }, - { "tls", TLS }, - { "tls-require", TLS_REQUIRE }, - { "ttl", TTL }, - { "user", USER }, - { "userbase", USERBASE }, - { "verify", VERIFY }, - { "virtual", VIRTUAL }, - { "warn-interval", WARN_INTERVAL }, - { "wrapper", WRAPPER }, - }; - const struct keywords *p; - - p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), - sizeof(keywords[0]), kw_cmp); - - if (p) - return (p->k_val); - else - return (STRING); -} - -#define START_EXPAND 1 -#define DONE_EXPAND 2 - -static int expanding; - -int -igetc(void) -{ - int c; - - while (1) { - if (file->ungetpos > 0) - c = file->ungetbuf[--file->ungetpos]; - else - c = getc(file->stream); - - if (c == START_EXPAND) - expanding = 1; - else if (c == DONE_EXPAND) - expanding = 0; - else - break; - } - return (c); -} - -int -lgetc(int quotec) -{ - int c, next; - - if (quotec) { - if ((c = igetc()) == EOF) { - yyerror("reached end of file while parsing " - "quoted string"); - if (file == topfile || popfile() == EOF) - return (EOF); - return (quotec); - } - return (c); - } - - while ((c = igetc()) == '\\') { - next = igetc(); - if (next != '\n') { - c = next; - break; - } - yylval.lineno = file->lineno; - file->lineno++; - } - - if (c == EOF) { - /* - * Fake EOL when hit EOF for the first time. This gets line - * count right if last line in included file is syntactically - * invalid and has no newline. - */ - if (file->eof_reached == 0) { - file->eof_reached = 1; - return ('\n'); - } - while (c == EOF) { - if (file == topfile || popfile() == EOF) - return (EOF); - c = igetc(); - } - } - return (c); -} - -void -lungetc(int c) -{ - if (c == EOF) - return; - - if (file->ungetpos >= file->ungetsize) { - void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); - if (p == NULL) - err(1, "%s", __func__); - file->ungetbuf = p; - file->ungetsize *= 2; - } - file->ungetbuf[file->ungetpos++] = c; -} - -int -findeol(void) -{ - int c; - - /* skip to either EOF or the first real EOL */ - while (1) { - c = lgetc(0); - if (c == '\n') { - file->lineno++; - break; - } - if (c == EOF) - break; - } - return (ERROR); -} - -int -yylex(void) -{ - unsigned char buf[8096]; - unsigned char *p, *val; - int quotec, next, c; - int token; - -top: - p = buf; - while ((c = lgetc(0)) == ' ' || c == '\t') - ; /* nothing */ - - yylval.lineno = file->lineno; - if (c == '#') - while ((c = lgetc(0)) != '\n' && c != EOF) - ; /* nothing */ - if (c == '$' && !expanding) { - while (1) { - if ((c = lgetc(0)) == EOF) - return (0); - - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - if (isalnum(c) || c == '_') { - *p++ = c; - continue; - } - *p = '\0'; - lungetc(c); - break; - } - val = symget(buf); - if (val == NULL) { - yyerror("macro '%s' not defined", buf); - return (findeol()); - } - p = val + strlen(val) - 1; - lungetc(DONE_EXPAND); - while (p >= val) { - lungetc(*p); - p--; - } - lungetc(START_EXPAND); - goto top; - } - - switch (c) { - case '\'': - case '"': - quotec = c; - while (1) { - if ((c = lgetc(quotec)) == EOF) - return (0); - if (c == '\n') { - file->lineno++; - continue; - } else if (c == '\\') { - if ((next = lgetc(quotec)) == EOF) - return (0); - if (next == quotec || next == ' ' || - next == '\t') - c = next; - else if (next == '\n') { - file->lineno++; - continue; - } else - lungetc(next); - } else if (c == quotec) { - *p = '\0'; - break; - } else if (c == '\0') { - yyerror("syntax error"); - return (findeol()); - } - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - *p++ = c; - } - yylval.v.string = strdup(buf); - if (yylval.v.string == NULL) - err(1, "%s", __func__); - return (STRING); - } - -#define allowed_to_end_number(x) \ - (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') - - if (c == '-' || isdigit(c)) { - do { - *p++ = c; - if ((size_t)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && isdigit(c)); - lungetc(c); - if (p == buf + 1 && buf[0] == '-') - goto nodigits; - if (c == EOF || allowed_to_end_number(c)) { - const char *errstr = NULL; - - *p = '\0'; - yylval.v.number = strtonum(buf, LLONG_MIN, - LLONG_MAX, &errstr); - if (errstr) { - yyerror("\"%s\" invalid number: %s", - buf, errstr); - return (findeol()); - } - return (NUMBER); - } else { -nodigits: - while (p > buf + 1) - lungetc(*--p); - c = *--p; - if (c == '-') - return (c); - } - } - - if (c == '=') { - if ((c = lgetc(0)) != EOF && c == '>') - return (ARROW); - lungetc(c); - c = '='; - } - -#define allowed_in_string(x) \ - (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ - x != '{' && x != '}' && x != '<' && x != '>' && \ - x != '!' && x != '=' && x != '#' && \ - x != ',')) - - if (isalnum(c) || c == ':' || c == '_') { - do { - *p++ = c; - if ((size_t)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); - lungetc(c); - *p = '\0'; - if ((token = lookup(buf)) == STRING) - if ((yylval.v.string = strdup(buf)) == NULL) - err(1, "%s", __func__); - return (token); - } - if (c == '\n') { - yylval.lineno = file->lineno; - file->lineno++; - } - if (c == EOF) - return (0); - return (c); -} - -int -check_file_secrecy(int fd, const char *fname) -{ - struct stat st; - - if (fstat(fd, &st)) { - log_warn("warn: cannot stat %s", fname); - return (-1); - } - if (st.st_uid != 0 && st.st_uid != getuid()) { - log_warnx("warn: %s: owner not root or current user", fname); - return (-1); - } - if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { - log_warnx("warn: %s: group/world readable/writeable", fname); - return (-1); - } - return (0); -} - -struct file * -pushfile(const char *name, int secret) -{ - struct file *nfile; - - if ((nfile = calloc(1, sizeof(struct file))) == NULL) { - log_warn("%s", __func__); - return (NULL); - } - if ((nfile->name = strdup(name)) == NULL) { - log_warn("%s", __func__); - free(nfile); - return (NULL); - } - if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { - log_warn("%s: %s", __func__, nfile->name); - free(nfile->name); - free(nfile); - return (NULL); - } else if (secret && - check_file_secrecy(fileno(nfile->stream), nfile->name)) { - fclose(nfile->stream); - free(nfile->name); - free(nfile); - return (NULL); - } - nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; - nfile->ungetsize = 16; - nfile->ungetbuf = malloc(nfile->ungetsize); - if (nfile->ungetbuf == NULL) { - log_warn("%s", __func__); - fclose(nfile->stream); - free(nfile->name); - free(nfile); - return (NULL); - } - TAILQ_INSERT_TAIL(&files, nfile, entry); - return (nfile); -} - -int -popfile(void) -{ - struct file *prev; - - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) - prev->errors += file->errors; - - TAILQ_REMOVE(&files, file, entry); - fclose(file->stream); - free(file->name); - free(file->ungetbuf); - free(file); - file = prev; - return (file ? 0 : EOF); -} - -int -parse_config(struct smtpd *x_conf, const char *filename, int opts) -{ - struct sym *sym, *next; - - conf = x_conf; - errors = 0; - - if ((file = pushfile(filename, 0)) == NULL) { - purge_config(PURGE_EVERYTHING); - return (-1); - } - topfile = file; - - /* - * parse configuration - */ - setservent(1); - yyparse(); - errors = file->errors; - popfile(); - endservent(); - - /* If the socket listener was not configured, create a default one. */ - if (!conf->sc_sock_listener) { - memset(&listen_opts, 0, sizeof listen_opts); - create_sock_listener(&listen_opts); - } - - /* Free macros and check which have not been used. */ - TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { - if ((conf->sc_opts & SMTPD_OPT_VERBOSE) && !sym->used) - fprintf(stderr, "warning: macro '%s' not " - "used\n", sym->nam); - if (!sym->persist) { - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entry); - free(sym); - } - } - - if (TAILQ_EMPTY(conf->sc_rules)) { - log_warnx("warn: no rules, nothing to do"); - errors++; - } - - if (errors) { - purge_config(PURGE_EVERYTHING); - return (-1); - } - - return (0); -} - -int -symset(const char *nam, const char *val, int persist) -{ - struct sym *sym; - - TAILQ_FOREACH(sym, &symhead, entry) { - if (strcmp(nam, sym->nam) == 0) - break; - } - - if (sym != NULL) { - if (sym->persist == 1) - return (0); - else { - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entry); - free(sym); - } - } - if ((sym = calloc(1, sizeof(*sym))) == NULL) - return (-1); - - sym->nam = strdup(nam); - if (sym->nam == NULL) { - free(sym); - return (-1); - } - sym->val = strdup(val); - if (sym->val == NULL) { - free(sym->nam); - free(sym); - return (-1); - } - sym->used = 0; - sym->persist = persist; - TAILQ_INSERT_TAIL(&symhead, sym, entry); - return (0); -} - -int -cmdline_symset(char *s) -{ - char *sym, *val; - int ret; - - if ((val = strrchr(s, '=')) == NULL) - return (-1); - sym = strndup(s, val - s); - if (sym == NULL) - errx(1, "%s: strndup", __func__); - ret = symset(sym, val + 1, 1); - free(sym); - - return (ret); -} - -char * -symget(const char *nam) -{ - struct sym *sym; - - TAILQ_FOREACH(sym, &symhead, entry) { - if (strcmp(nam, sym->nam) == 0) { - sym->used = 1; - return (sym->val); - } - } - return (NULL); -} - -static void -create_sock_listener(struct listen_opts *lo) -{ - struct listener *l = xcalloc(1, sizeof(*l)); - lo->hostname = conf->sc_hostname; - l->ss.ss_family = AF_LOCAL; -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN - l->ss.ss_len = sizeof(struct sockaddr *); -#endif - l->local = 1; - conf->sc_sock_listener = l; - config_listener(l, lo); -} - -static void -create_if_listener(struct listen_opts *lo) -{ - uint16_t flags; - - if (lo->port != 0 && lo->ssl == F_SSL) - errx(1, "invalid listen option: tls/smtps on same port"); - - if (lo->auth != 0 && !lo->ssl) - errx(1, "invalid listen option: auth requires tls/smtps"); - - if (lo->pki && !lo->ssl) - errx(1, "invalid listen option: pki requires tls/smtps"); - - flags = lo->flags; - - if (lo->port) { - lo->flags = lo->ssl|lo->auth|flags; - lo->port = htons(lo->port); - } - else { - if (lo->ssl & F_SMTPS) { - lo->port = htons(465); - lo->flags = F_SMTPS|lo->auth|flags; - } - - if (!lo->ssl || (lo->ssl & F_STARTTLS)) { - lo->port = htons(25); - lo->flags = lo->auth|flags; - if (lo->ssl & F_STARTTLS) - lo->flags |= F_STARTTLS; - } - } - - if (interface(lo)) - return; - if (host_v4(lo)) - return; - if (host_v6(lo)) - return; - if (host_dns(lo)) - return; - - errx(1, "invalid virtual ip or interface: %s", lo->ifx); -} - -static void -config_listener(struct listener *h, struct listen_opts *lo) -{ - h->fd = -1; - h->port = lo->port; - h->flags = lo->flags; - - if (lo->hostname == NULL) - lo->hostname = conf->sc_hostname; - - if (lo->options & LO_FILTER) { - h->flags |= F_FILTERED; - (void)strlcpy(h->filter_name, - lo->filtername, - sizeof(h->filter_name)); - } - - h->pki_name[0] = '\0'; - - if (lo->authtable != NULL) - (void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable)); - if (lo->pki != NULL) { - if (!lowercase(h->pki_name, lo->pki, sizeof(h->pki_name))) { - log_warnx("pki name too long: %s", lo->pki); - fatalx(NULL); - } - if (dict_get(conf->sc_pki_dict, h->pki_name) == NULL) { - log_warnx("pki name not found: %s", lo->pki); - fatalx(NULL); - } - } - - if (lo->ca != NULL) { - if (!lowercase(h->ca_name, lo->ca, sizeof(h->ca_name))) { - log_warnx("ca name too long: %s", lo->ca); - fatalx(NULL); - } - if (dict_get(conf->sc_ca_dict, h->ca_name) == NULL) { - log_warnx("ca name not found: %s", lo->ca); - fatalx(NULL); - } - } - if (lo->tag != NULL) - (void)strlcpy(h->tag, lo->tag, sizeof(h->tag)); - - (void)strlcpy(h->hostname, lo->hostname, sizeof(h->hostname)); - if (lo->hostnametable) - (void)strlcpy(h->hostnametable, lo->hostnametable->t_name, sizeof(h->hostnametable)); - if (lo->sendertable) { - (void)strlcpy(h->sendertable, lo->sendertable->t_name, sizeof(h->sendertable)); - if (lo->options & LO_MASQUERADE) - h->flags |= F_MASQUERADE; - } - - if (lo->ssl & F_TLS_VERIFY) - h->flags |= F_TLS_VERIFY; - - if (lo->ssl & F_STARTTLS_REQUIRE) - h->flags |= F_STARTTLS_REQUIRE; - - if (h != conf->sc_sock_listener) - TAILQ_INSERT_TAIL(conf->sc_listeners, h, entry); -} - -static int -host_v4(struct listen_opts *lo) -{ - struct in_addr ina; - struct sockaddr_in *sain; - struct listener *h; - - if (lo->family != AF_UNSPEC && lo->family != AF_INET) - return (0); - - memset(&ina, 0, sizeof(ina)); - if (inet_pton(AF_INET, lo->ifx, &ina) != 1) - return (0); - - h = xcalloc(1, sizeof(*h)); - sain = (struct sockaddr_in *)&h->ss; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sain->sin_len = sizeof(struct sockaddr_in); -#endif - sain->sin_family = AF_INET; - sain->sin_addr.s_addr = ina.s_addr; - sain->sin_port = lo->port; - - if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) - h->local = 1; - config_listener(h, lo); - - return (1); -} - -static int -host_v6(struct listen_opts *lo) -{ - struct in6_addr ina6; - struct sockaddr_in6 *sin6; - struct listener *h; - - if (lo->family != AF_UNSPEC && lo->family != AF_INET6) - return (0); - - memset(&ina6, 0, sizeof(ina6)); - if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1) - return (0); - - h = xcalloc(1, sizeof(*h)); - sin6 = (struct sockaddr_in6 *)&h->ss; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - sin6->sin6_family = AF_INET6; - sin6->sin6_port = lo->port; - memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6)); - - if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) - h->local = 1; - config_listener(h, lo); - - return (1); -} - -static int -host_dns(struct listen_opts *lo) -{ - struct addrinfo hints, *res0, *res; - int error, cnt = 0; - struct sockaddr_in *sain; - struct sockaddr_in6 *sin6; - struct listener *h; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = lo->family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - error = getaddrinfo(lo->ifx, NULL, &hints, &res0); - if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) - return (0); - if (error) { - log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx, - gai_strerror(error)); - return (-1); - } - - for (res = res0; res; res = res->ai_next) { - if (res->ai_family != AF_INET && - res->ai_family != AF_INET6) - continue; - h = xcalloc(1, sizeof(*h)); - - h->ss.ss_family = res->ai_family; - if (res->ai_family == AF_INET) { - sain = (struct sockaddr_in *)&h->ss; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sain->sin_len = sizeof(struct sockaddr_in); -#endif - sain->sin_addr.s_addr = ((struct sockaddr_in *) - res->ai_addr)->sin_addr.s_addr; - sain->sin_port = lo->port; - if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) - h->local = 1; - } else { - sin6 = (struct sockaddr_in6 *)&h->ss; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) - res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); - sin6->sin6_port = lo->port; - if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) - h->local = 1; - } - - config_listener(h, lo); - - cnt++; - } - - freeaddrinfo(res0); - return (cnt); -} - -static int -interface(struct listen_opts *lo) -{ - struct ifaddrs *ifap, *p; - struct sockaddr_in *sain; - struct sockaddr_in6 *sin6; - struct listener *h; - int ret = 0; - - if (getifaddrs(&ifap) == -1) - fatal("getifaddrs"); - - for (p = ifap; p != NULL; p = p->ifa_next) { - if (p->ifa_addr == NULL) - continue; - if (strcmp(p->ifa_name, lo->ifx) != 0 && - !is_if_in_group(p->ifa_name, lo->ifx)) - continue; - if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family) - continue; - - h = xcalloc(1, sizeof(*h)); - - switch (p->ifa_addr->sa_family) { - case AF_INET: - sain = (struct sockaddr_in *)&h->ss; - *sain = *(struct sockaddr_in *)p->ifa_addr; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sain->sin_len = sizeof(struct sockaddr_in); -#endif - sain->sin_port = lo->port; - if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) - h->local = 1; - break; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *)&h->ss; - *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - sin6->sin6_port = lo->port; - if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) - h->local = 1; - break; - - default: - free(h); - continue; - } - - config_listener(h, lo); - ret = 1; - } - - freeifaddrs(ifap); - - return ret; -} - -int -delaytonum(char *str) -{ - unsigned int factor; - size_t len; - const char *errstr = NULL; - int delay; - - /* we need at least 1 digit and 1 unit */ - len = strlen(str); - if (len < 2) - goto bad; - - switch(str[len - 1]) { - - case 's': - factor = 1; - break; - - case 'm': - factor = 60; - break; - - case 'h': - factor = 60 * 60; - break; - - case 'd': - factor = 24 * 60 * 60; - break; - - default: - goto bad; - } - - str[len - 1] = '\0'; - delay = strtonum(str, 1, INT_MAX / factor, &errstr); - if (errstr) - goto bad; - - return (delay * factor); - -bad: - return (-1); -} - -int -is_if_in_group(const char *ifname, const char *groupname) -{ -#ifdef HAVE_STRUCT_IFGROUPREQ - unsigned int len; - struct ifgroupreq ifgr; - struct ifg_req *ifg; - int s; - int ret = 0; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - err(1, "socket"); - - memset(&ifgr, 0, sizeof(ifgr)); - if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ) - errx(1, "interface name too large"); - - if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { - if (errno == EINVAL || errno == ENOTTY) - goto end; - err(1, "SIOCGIFGROUP"); - } - - len = ifgr.ifgr_len; - ifgr.ifgr_groups = xcalloc(len/sizeof(struct ifg_req), - sizeof(struct ifg_req)); - if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) - err(1, "SIOCGIFGROUP"); - - ifg = ifgr.ifgr_groups; - for (; ifg && len >= sizeof(struct ifg_req); ifg++) { - len -= sizeof(struct ifg_req); - if (strcmp(ifg->ifgrq_group, groupname) == 0) { - ret = 1; - break; - } - } - free(ifgr.ifgr_groups); - -end: - close(s); - return ret; -#else - return (0); -#endif -} - -static int -config_lo_mask_source(struct listen_opts *lo) { - if (lo->options & LO_MASKSOURCE) { - yyerror("mask-source already specified"); - return -1; - } - lo->options |= LO_MASKSOURCE; - lo->flags |= F_MASK_SOURCE; - - return 0; -} - diff --git a/smtpd/parser.c b/smtpd/parser.c deleted file mode 100644 index 4c2321ec..00000000 --- a/smtpd/parser.c +++ /dev/null @@ -1,341 +0,0 @@ -/* $OpenBSD: parser.c,v 1.42 2020/01/06 11:02:38 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/socket.h> - -#include <netinet/in.h> -#include <net/if.h> -#include <arpa/inet.h> - -#include <err.h> -#include <inttypes.h> -#include <limits.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "parser.h" - -uint64_t text_to_evpid(const char *); -uint32_t text_to_msgid(const char *); - -struct node { - int type; - const char *token; - struct node *parent; - TAILQ_ENTRY(node) entry; - TAILQ_HEAD(, node) children; - int (*cmd)(int, struct parameter*); -}; - -static struct node *root; - -static int text_to_sockaddr(struct sockaddr *, int, const char *); - -#define ARGVMAX 64 - -int -cmd_install(const char *pattern, int (*cmd)(int, struct parameter*)) -{ - struct node *node, *tmp; - char *s, *str, *argv[ARGVMAX], **ap; - int i, n; - - /* Tokenize */ - str = s = strdup(pattern); - if (str == NULL) - err(1, "strdup"); - n = 0; - for (ap = argv; n < ARGVMAX && (*ap = strsep(&str, " \t")) != NULL;) { - if (**ap != '\0') { - ap++; - n++; - } - } - *ap = NULL; - - if (root == NULL) { - root = calloc(1, sizeof (*root)); - TAILQ_INIT(&root->children); - } - node = root; - - for (i = 0; i < n; i++) { - TAILQ_FOREACH(tmp, &node->children, entry) { - if (!strcmp(tmp->token, argv[i])) { - node = tmp; - break; - } - } - if (tmp == NULL) { - tmp = calloc(1, sizeof (*tmp)); - TAILQ_INIT(&tmp->children); - if (!strcmp(argv[i], "<str>")) - tmp->type = P_STR; - else if (!strcmp(argv[i], "<int>")) - tmp->type = P_INT; - else if (!strcmp(argv[i], "<msgid>")) - tmp->type = P_MSGID; - else if (!strcmp(argv[i], "<evpid>")) - tmp->type = P_EVPID; - else if (!strcmp(argv[i], "<routeid>")) - tmp->type = P_ROUTEID; - else if (!strcmp(argv[i], "<addr>")) - tmp->type = P_ADDR; - else - tmp->type = P_TOKEN; - tmp->token = strdup(argv[i]); - tmp->parent = node; - TAILQ_INSERT_TAIL(&node->children, tmp, entry); - node = tmp; - } - } - - if (node->cmd) - errx(1, "duplicate pattern: %s", pattern); - node->cmd = cmd; - - free(s); - return (n); -} - -static int -cmd_check(const char *str, struct node *node, struct parameter *res) -{ - const char *e; - - switch (node->type) { - case P_TOKEN: - if (!strcmp(str, node->token)) - return (1); - return (0); - - case P_STR: - res->u.u_str = str; - return (1); - - case P_INT: - res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e); - if (e) - return (0); - return (1); - - case P_MSGID: - if (strlen(str) != 8) - return (0); - res->u.u_msgid = text_to_msgid(str); - if (res->u.u_msgid == 0) - return (0); - return (1); - - case P_EVPID: - if (strlen(str) != 16) - return (0); - res->u.u_evpid = text_to_evpid(str); - if (res->u.u_evpid == 0) - return (0); - return (1); - - case P_ROUTEID: - res->u.u_routeid = strtonum(str, 1, LLONG_MAX, &e); - if (e) - return (0); - return (1); - - case P_ADDR: - if (text_to_sockaddr((struct sockaddr *)&res->u.u_ss, PF_UNSPEC, str) == 0) - return (1); - return (0); - - default: - errx(1, "bad token type: %d", node->type); - return (0); - } -} - -int -cmd_run(int argc, char **argv) -{ - struct parameter param[ARGVMAX]; - struct node *node, *tmp, *stack[ARGVMAX], *best; - int i, j, np; - - node = root; - np = 0; - - for (i = 0; i < argc; i++) { - TAILQ_FOREACH(tmp, &node->children, entry) { - if (cmd_check(argv[i], tmp, ¶m[np])) { - stack[i] = tmp; - node = tmp; - param[np].type = node->type; - if (node->type != P_TOKEN) - np++; - break; - } - } - if (tmp == NULL) { - best = NULL; - TAILQ_FOREACH(tmp, &node->children, entry) { - if (tmp->type != P_TOKEN) - continue; - if (strstr(tmp->token, argv[i]) != tmp->token) - continue; - if (best) - goto fail; - best = tmp; - } - if (best == NULL) - goto fail; - stack[i] = best; - node = best; - param[np].type = node->type; - if (node->type != P_TOKEN) - np++; - } - } - - if (node->cmd == NULL) - goto fail; - - return (node->cmd(np, np ? param : NULL)); - -fail: - if (TAILQ_FIRST(&node->children) == NULL) { - fprintf(stderr, "invalid command\n"); - return (-1); - } - - fprintf(stderr, "possibilities are:\n"); - TAILQ_FOREACH(tmp, &node->children, entry) { - for (j = 0; j < i; j++) - fprintf(stderr, "%s%s", j?" ":"", stack[j]->token); - fprintf(stderr, "%s%s\n", i?" ":"", tmp->token); - } - - return (-1); -} - -int -cmd_show_params(int argc, struct parameter *argv) -{ - int i; - - for (i = 0; i < argc; i++) { - switch(argv[i].type) { - case P_STR: - printf(" str:\"%s\"", argv[i].u.u_str); - break; - case P_INT: - printf(" int:%d", argv[i].u.u_int); - break; - case P_MSGID: - printf(" msgid:%08"PRIx32, argv[i].u.u_msgid); - break; - case P_EVPID: - printf(" evpid:%016"PRIx64, argv[i].u.u_evpid); - break; - case P_ROUTEID: - printf(" routeid:%016"PRIx64, argv[i].u.u_routeid); - break; - default: - printf(" ???:%d", argv[i].type); - } - } - printf ("\n"); - return (1); -} - -static int -text_to_sockaddr(struct sockaddr *sa, int family, const char *str) -{ - struct in_addr ina; - struct in6_addr in6a; - struct sockaddr_in *in; - struct sockaddr_in6 *in6; - char *cp, *str2; - const char *errstr; - - switch (family) { - case PF_UNSPEC: - if (text_to_sockaddr(sa, PF_INET, str) == 0) - return (0); - return text_to_sockaddr(sa, PF_INET6, str); - - case PF_INET: - if (inet_pton(PF_INET, str, &ina) != 1) - return (-1); - - in = (struct sockaddr_in *)sa; - memset(in, 0, sizeof *in); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - in->sin_len = sizeof(struct sockaddr_in); -#endif - in->sin_family = PF_INET; - in->sin_addr.s_addr = ina.s_addr; - return (0); - - case PF_INET6: - cp = strchr(str, SCOPE_DELIMITER); - if (cp) { - str2 = strdup(str); - if (str2 == NULL) - return (-1); - str2[cp - str] = '\0'; - if (inet_pton(PF_INET6, str2, &in6a) != 1) { - free(str2); - return (-1); - } - cp++; - free(str2); - } else if (inet_pton(PF_INET6, str, &in6a) != 1) - return (-1); - - in6 = (struct sockaddr_in6 *)sa; - memset(in6, 0, sizeof *in6); -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - in6->sin6_len = sizeof(struct sockaddr_in6); -#endif - in6->sin6_family = PF_INET6; - in6->sin6_addr = in6a; - - if (cp == NULL) - return (0); - - if (IN6_IS_ADDR_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_NODELOCAL(&in6a)) - if ((in6->sin6_scope_id = if_nametoindex(cp))) - return (0); - - in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); - if (errstr) - return (-1); - return (0); - - default: - break; - } - - return (-1); -} diff --git a/smtpd/parser.h b/smtpd/parser.h deleted file mode 100644 index f0114e9e..00000000 --- a/smtpd/parser.h +++ /dev/null @@ -1,43 +0,0 @@ -/* $OpenBSD: parser.h,v 1.29 2014/02/04 15:22:39 eric Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -enum { - P_TOKEN, - P_STR, - P_INT, - P_MSGID, - P_EVPID, - P_ROUTEID, - P_ADDR, -}; - -struct parameter { - int type; - union { - const char *u_str; - int u_int; - uint32_t u_msgid; - uint64_t u_evpid; - uint64_t u_routeid; - struct sockaddr_storage u_ss; - } u; -}; - -int cmd_install(const char *, int (*)(int, struct parameter *)); -int cmd_run(int, char **); -int cmd_show_params(int argc, struct parameter *argv); diff --git a/smtpd/pony.c b/smtpd/pony.c deleted file mode 100644 index 1865b339..00000000 --- a/smtpd/pony.c +++ /dev/null @@ -1,212 +0,0 @@ -/* $OpenBSD: pony.c,v 1.27 2019/06/13 11:45:35 eric Exp $ */ - -/* - * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> -#include <grp.h> - -#include "smtpd.h" -#include "log.h" - -void mda_imsg(struct mproc *, struct imsg *); -void mta_imsg(struct mproc *, struct imsg *); -void smtp_imsg(struct mproc *, struct imsg *); - -static void pony_shutdown(void); - -void -pony_imsg(struct mproc *p, struct imsg *imsg) -{ - struct msg m; - int v; - - if (imsg == NULL) - pony_shutdown(); - - switch (imsg->hdr.type) { - - case IMSG_GETADDRINFO: - case IMSG_GETADDRINFO_END: - case IMSG_GETNAMEINFO: - case IMSG_RES_QUERY: - resolver_dispatch_result(p, imsg); - return; - - case IMSG_CERT_INIT: - case IMSG_CERT_VERIFY: - cert_dispatch_result(p, imsg); - return; - - case IMSG_CONF_START: - return; - case IMSG_CONF_END: - smtp_configure(); - return; - case IMSG_CTL_VERBOSE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - log_trace_verbose(v); - return; - case IMSG_CTL_PROFILE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - profiling = v; - return; - - /* smtp imsg */ - case IMSG_SMTP_CHECK_SENDER: - case IMSG_SMTP_EXPAND_RCPT: - case IMSG_SMTP_LOOKUP_HELO: - case IMSG_SMTP_AUTHENTICATE: - case IMSG_SMTP_MESSAGE_COMMIT: - case IMSG_SMTP_MESSAGE_CREATE: - case IMSG_SMTP_MESSAGE_OPEN: - case IMSG_FILTER_SMTP_PROTOCOL: - case IMSG_FILTER_SMTP_DATA_BEGIN: - case IMSG_QUEUE_ENVELOPE_SUBMIT: - case IMSG_QUEUE_ENVELOPE_COMMIT: - case IMSG_QUEUE_SMTP_SESSION: - case IMSG_CTL_SMTP_SESSION: - case IMSG_CTL_PAUSE_SMTP: - case IMSG_CTL_RESUME_SMTP: - smtp_imsg(p, imsg); - return; - - /* mta imsg */ - case IMSG_QUEUE_TRANSFER: - case IMSG_MTA_OPEN_MESSAGE: - case IMSG_MTA_LOOKUP_CREDENTIALS: - case IMSG_MTA_LOOKUP_SMARTHOST: - case IMSG_MTA_LOOKUP_SOURCE: - case IMSG_MTA_LOOKUP_HELO: - case IMSG_MTA_DNS_HOST: - case IMSG_MTA_DNS_HOST_END: - case IMSG_MTA_DNS_MX_PREFERENCE: - case IMSG_CTL_RESUME_ROUTE: - case IMSG_CTL_MTA_SHOW_HOSTS: - case IMSG_CTL_MTA_SHOW_RELAYS: - case IMSG_CTL_MTA_SHOW_ROUTES: - case IMSG_CTL_MTA_SHOW_HOSTSTATS: - case IMSG_CTL_MTA_BLOCK: - case IMSG_CTL_MTA_UNBLOCK: - case IMSG_CTL_MTA_SHOW_BLOCK: - mta_imsg(p, imsg); - return; - - /* mda imsg */ - case IMSG_MDA_LOOKUP_USERINFO: - case IMSG_QUEUE_DELIVER: - case IMSG_MDA_OPEN_MESSAGE: - case IMSG_MDA_FORK: - case IMSG_MDA_DONE: - mda_imsg(p, imsg); - return; - default: - break; - } - - errx(1, "session_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -static void -pony_shutdown(void) -{ - log_debug("debug: pony agent exiting"); - _exit(0); -} - -int -pony(void) -{ - struct passwd *pw; - - mda_postfork(); - mta_postfork(); - smtp_postfork(); - - /* do not purge listeners and pki, they are purged - * in smtp_configure() - */ - purge_config(PURGE_TABLES|PURGE_RULES); - - if ((pw = getpwnam(SMTPD_USER)) == NULL) - fatalx("unknown user " SMTPD_USER); - - if (chroot(PATH_CHROOT) == -1) - fatal("pony: chroot"); - if (chdir("/") == -1) - fatal("pony: chdir(\"/\")"); - - config_process(PROC_PONY); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("pony: cannot drop privileges"); - - imsg_callback = pony_imsg; - event_init(); - - mda_postprivdrop(); - mta_postprivdrop(); - smtp_postprivdrop(); - - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - - config_peer(PROC_PARENT); - config_peer(PROC_QUEUE); - config_peer(PROC_LKA); - config_peer(PROC_CONTROL); - config_peer(PROC_CA); - - ca_engine_init(); - -#if HAVE_PLEDGE - if (pledge("stdio inet unix recvfd sendfd", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} diff --git a/smtpd/proxy.c b/smtpd/proxy.c deleted file mode 100644 index fdaf4f27..00000000 --- a/smtpd/proxy.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2017 Antoine Kaufmann <toni@famkaufmann.info> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/un.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "log.h" -#include "smtpd.h" - - -/* - * The PROXYv2 protocol is described here: - * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt - */ - -#define PROXY_CLOCAL 0x0 -#define PROXY_CPROXY 0x1 -#define PROXY_AF_UNSPEC 0x0 -#define PROXY_AF_INET 0x1 -#define PROXY_AF_INET6 0x2 -#define PROXY_AF_UNIX 0x3 -#define PROXY_TF_UNSPEC 0x0 -#define PROXY_TF_STREAM 0x1 -#define PROXY_TF_DGRAM 0x2 - -#define PROXY_SESSION_TIMEOUT 300 - -static const uint8_t pv2_signature[] = { - 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, - 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A -}; - -struct proxy_hdr_v2 { - uint8_t sig[12]; - uint8_t ver_cmd; - uint8_t fam; - uint16_t len; -} __attribute__((packed)); - - -struct proxy_addr_ipv4 { - uint32_t src_addr; - uint32_t dst_addr; - uint16_t src_port; - uint16_t dst_port; -} __attribute__((packed)); - -struct proxy_addr_ipv6 { - uint8_t src_addr[16]; - uint8_t dst_addr[16]; - uint16_t src_port; - uint16_t dst_port; -} __attribute__((packed)); - -struct proxy_addr_unix { - uint8_t src_addr[108]; - uint8_t dst_addr[108]; -} __attribute__((packed)); - -union proxy_addr { - struct proxy_addr_ipv4 ipv4; - struct proxy_addr_ipv6 ipv6; - struct proxy_addr_unix un; -} __attribute__((packed)); - -struct proxy_session { - struct listener *l; - struct io *io; - - uint64_t id; - int fd; - uint16_t header_len; - uint16_t header_total; - uint16_t addr_len; - uint16_t addr_total; - - struct sockaddr_storage ss; - struct proxy_hdr_v2 hdr; - union proxy_addr addr; - - void (*cb_accepted)(struct listener *, int, - const struct sockaddr_storage *, struct io *); - void (*cb_dropped)(struct listener *, int, - const struct sockaddr_storage *); -}; - -static void proxy_io(struct io *, int, void *); -static void proxy_error(struct proxy_session *, const char *, const char *); -static int proxy_header_validate(struct proxy_session *); -static int proxy_translate_ss(struct proxy_session *); - -int -proxy_session(struct listener *listener, int sock, - const struct sockaddr_storage *ss, - void (*accepted)(struct listener *, int, - const struct sockaddr_storage *, struct io *), - void (*dropped)(struct listener *, int, - const struct sockaddr_storage *)); - -int -proxy_session(struct listener *listener, int sock, - const struct sockaddr_storage *ss, - void (*accepted)(struct listener *, int, - const struct sockaddr_storage *, struct io *), - void (*dropped)(struct listener *, int, - const struct sockaddr_storage *)) -{ - struct proxy_session *s; - - if ((s = calloc(1, sizeof(*s))) == NULL) - return (-1); - - if ((s->io = io_new()) == NULL) { - free(s); - return (-1); - } - - s->id = generate_uid(); - s->l = listener; - s->fd = sock; - s->header_len = 0; - s->addr_len = 0; - s->ss = *ss; - s->cb_accepted = accepted; - s->cb_dropped = dropped; - - io_set_callback(s->io, proxy_io, s); - io_set_fd(s->io, sock); - io_set_timeout(s->io, PROXY_SESSION_TIMEOUT * 1000); - io_set_read(s->io); - - log_info("%016"PRIx64" smtp event=proxy address=%s", - s->id, ss_to_text(&s->ss)); - - return 0; -} - -static void -proxy_io(struct io *io, int evt, void *arg) -{ - struct proxy_session *s = arg; - struct proxy_hdr_v2 *h = &s->hdr; - uint8_t *buf; - size_t len, off; - - switch (evt) { - - case IO_DATAIN: - buf = io_data(io); - len = io_datalen(io); - - if (s->header_len < sizeof(s->hdr)) { - /* header is incomplete */ - off = sizeof(s->hdr) - s->header_len; - off = (len < off ? len : off); - memcpy((uint8_t *) &s->hdr + s->header_len, buf, off); - - s->header_len += off; - buf += off; - len -= off; - io_drop(s->io, off); - - if (s->header_len < sizeof(s->hdr)) { - /* header is still not complete */ - return; - } - - if (proxy_header_validate(s) != 0) - return; - } - - if (s->addr_len < s->addr_total) { - /* address is incomplete */ - off = s->addr_total - s->addr_len; - off = (len < off ? len : off); - memcpy((uint8_t *) &s->addr + s->addr_len, buf, off); - - s->header_len += off; - s->addr_len += off; - buf += off; - len -= off; - io_drop(s->io, off); - - if (s->addr_len < s->addr_total) { - /* address is still not complete */ - return; - } - } - - if (s->header_len < s->header_total) { - /* additional parameters not complete */ - /* these are ignored for now, but we still need to drop - * the bytes from the buffer */ - off = s->header_total - s->header_len; - off = (len < off ? len : off); - - s->header_len += off; - io_drop(s->io, off); - - if (s->header_len < s->header_total) - /* not complete yet */ - return; - } - - switch(h->ver_cmd & 0xF) { - case PROXY_CLOCAL: - /* local address, no need to modify ss */ - break; - - case PROXY_CPROXY: - if (proxy_translate_ss(s) != 0) - return; - break; - - default: - proxy_error(s, "protocol error", "unknown command"); - return; - } - - s->cb_accepted(s->l, s->fd, &s->ss, s->io); - /* we passed off s->io, so it does not need to be freed here */ - free(s); - break; - - case IO_TIMEOUT: - proxy_error(s, "timeout", NULL); - break; - - case IO_DISCONNECTED: - proxy_error(s, "disconnected", NULL); - break; - - case IO_ERROR: - proxy_error(s, "IO error", io_error(io)); - break; - - default: - fatalx("proxy_io()"); - } - -} - -static void -proxy_error(struct proxy_session *s, const char *reason, const char *extra) -{ - if (extra) - log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"", - s, ss_to_text(&s->ss), reason, extra); - else - log_info("proxy %p event=closed address=%s reason=\"%s\"", - s, ss_to_text(&s->ss), reason); - - s->cb_dropped(s->l, s->fd, &s->ss); - io_free(s->io); - free(s); -} - -static int -proxy_header_validate(struct proxy_session *s) -{ - struct proxy_hdr_v2 *h = &s->hdr; - - if (memcmp(h->sig, pv2_signature, - sizeof(pv2_signature)) != 0) { - proxy_error(s, "protocol error", "invalid signature"); - return (-1); - } - - if ((h->ver_cmd >> 4) != 2) { - proxy_error(s, "protocol error", "invalid version"); - return (-1); - } - - switch (h->fam) { - case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC): - s->addr_total = 0; - break; - - case (PROXY_AF_INET << 4 | PROXY_TF_STREAM): - s->addr_total = sizeof(s->addr.ipv4); - break; - - case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM): - s->addr_total = sizeof(s->addr.ipv6); - break; - - case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM): - s->addr_total = sizeof(s->addr.un); - break; - - default: - proxy_error(s, "protocol error", "unsupported address family"); - return (-1); - } - - s->header_total = ntohs(h->len); - if (s->header_total > UINT16_MAX - sizeof(struct proxy_hdr_v2)) { - proxy_error(s, "protocol error", "header too long"); - return (-1); - } - s->header_total += sizeof(struct proxy_hdr_v2); - - if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) { - proxy_error(s, "protocol error", "address info too short"); - return (-1); - } - - return 0; -} - -static int -proxy_translate_ss(struct proxy_session *s) -{ - struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss; - struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss; - size_t sun_len; - - switch (s->hdr.fam) { - case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC): - /* unspec: only supported for local */ - proxy_error(s, "address translation", "UNSPEC family not " - "supported for PROXYing"); - return (-1); - - case (PROXY_AF_INET << 4 | PROXY_TF_STREAM): - memset(&s->ss, 0, sizeof(s->ss)); - sin->sin_family = AF_INET; - sin->sin_port = s->addr.ipv4.src_port; - sin->sin_addr.s_addr = s->addr.ipv4.src_addr; - break; - - case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM): - memset(&s->ss, 0, sizeof(s->ss)); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = s->addr.ipv6.src_port; - memcpy(sin6->sin6_addr.s6_addr, s->addr.ipv6.src_addr, - sizeof(s->addr.ipv6.src_addr)); - break; - - case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM): - memset(&s->ss, 0, sizeof(s->ss)); - sun_len = strnlen(s->addr.un.src_addr, - sizeof(s->addr.un.src_addr)); - if (sun_len > sizeof(sun->sun_path)) { - proxy_error(s, "address translation", "Unix socket path" - " longer than supported"); - return (-1); - } - sun->sun_family = AF_UNIX; - memcpy(sun->sun_path, s->addr.un.src_addr, sun_len); - break; - - default: - fatalx("proxy_translate_ss()"); - } - - return 0; -} diff --git a/smtpd/queue.c b/smtpd/queue.c deleted file mode 100644 index 434e3647..00000000 --- a/smtpd/queue.c +++ /dev/null @@ -1,750 +0,0 @@ -/* $OpenBSD: queue.c,v 1.190 2020/04/22 11:35:34 eric Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <grp.h> /* needed for setgroups */ -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -static void queue_imsg(struct mproc *, struct imsg *); -static void queue_timeout(int, short, void *); -static void queue_bounce(struct envelope *, struct delivery_bounce *); -static void queue_shutdown(void); -static void queue_log(const struct envelope *, const char *, const char *); -static void queue_msgid_walk(int, short, void *); - - -static void -queue_imsg(struct mproc *p, struct imsg *imsg) -{ - struct delivery_bounce bounce; - struct msg_walkinfo *wi; - struct timeval tv; - struct bounce_req_msg *req_bounce; - struct envelope evp; - struct msg m; - const char *reason; - uint64_t reqid, evpid, holdq; - uint32_t msgid; - time_t nexttry; - size_t n_evp; - int fd, mta_ext, ret, v, flags, code; - - if (imsg == NULL) - queue_shutdown(); - - memset(&bounce, 0, sizeof(struct delivery_bounce)); - - switch (imsg->hdr.type) { - case IMSG_SMTP_MESSAGE_CREATE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - ret = queue_message_create(&msgid); - - m_create(p, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1); - m_add_id(p, reqid); - if (ret == 0) - m_add_int(p, 0); - else { - m_add_int(p, 1); - m_add_msgid(p, msgid); - } - m_close(p); - return; - - case IMSG_SMTP_MESSAGE_ROLLBACK: - m_msg(&m, imsg); - m_get_msgid(&m, &msgid); - m_end(&m); - - queue_message_delete(msgid); - - m_create(p_scheduler, IMSG_QUEUE_MESSAGE_ROLLBACK, - 0, 0, -1); - m_add_msgid(p_scheduler, msgid); - m_close(p_scheduler); - return; - - case IMSG_SMTP_MESSAGE_COMMIT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_msgid(&m, &msgid); - m_end(&m); - - ret = queue_message_commit(msgid); - - m_create(p, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, (ret == 0) ? 0 : 1); - m_close(p); - - if (ret) { - m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, - 0, 0, -1); - m_add_msgid(p_scheduler, msgid); - m_close(p_scheduler); - } - return; - - case IMSG_SMTP_MESSAGE_OPEN: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_msgid(&m, &msgid); - m_end(&m); - - fd = queue_message_fd_rw(msgid); - - m_create(p, IMSG_SMTP_MESSAGE_OPEN, 0, 0, fd); - m_add_id(p, reqid); - m_add_int(p, (fd == -1) ? 0 : 1); - m_close(p); - return; - - case IMSG_QUEUE_SMTP_SESSION: - bounce_fd(imsg->fd); - return; - - case IMSG_LKA_ENVELOPE_SUBMIT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_envelope(&m, &evp); - m_end(&m); - - if (evp.id == 0) - log_warnx("warn: imsg_queue_submit_envelope: evpid=0"); - if (evpid_to_msgid(evp.id) == 0) - log_warnx("warn: imsg_queue_submit_envelope: msgid=0, " - "evpid=%016"PRIx64, evp.id); - ret = queue_envelope_create(&evp); - m_create(p_pony, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); - m_add_id(p_pony, reqid); - if (ret == 0) - m_add_int(p_pony, 0); - else { - m_add_int(p_pony, 1); - m_add_evpid(p_pony, evp.id); - } - m_close(p_pony); - if (ret) { - m_create(p_scheduler, - IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); - m_add_envelope(p_scheduler, &evp); - m_close(p_scheduler); - } - return; - - case IMSG_LKA_ENVELOPE_COMMIT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - m_create(p_pony, IMSG_QUEUE_ENVELOPE_COMMIT, 0, 0, -1); - m_add_id(p_pony, reqid); - m_add_int(p_pony, 1); - m_close(p_pony); - return; - - case IMSG_SCHED_ENVELOPE_REMOVE: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_close(p_scheduler); - - /* already removed by scheduler */ - if (queue_envelope_load(evpid, &evp) == 0) - return; - - queue_log(&evp, "Remove", "Removed by administrator"); - queue_envelope_delete(evpid); - return; - - case IMSG_SCHED_ENVELOPE_EXPIRE: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_close(p_scheduler); - - /* already removed by scheduler*/ - if (queue_envelope_load(evpid, &evp) == 0) - return; - - bounce.type = B_FAILED; - envelope_set_errormsg(&evp, "Envelope expired"); - envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); - envelope_set_esc_code(&evp, ESC_DELIVERY_TIME_EXPIRED); - queue_bounce(&evp, &bounce); - queue_log(&evp, "Expire", "Envelope expired"); - queue_envelope_delete(evpid); - return; - - case IMSG_SCHED_ENVELOPE_BOUNCE: - CHECK_IMSG_DATA_SIZE(imsg, sizeof *req_bounce); - req_bounce = imsg->data; - evpid = req_bounce->evpid; - - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: bounce: failed to load envelope"); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_add_u32(p_scheduler, 0); /* not in-flight */ - m_close(p_scheduler); - return; - } - queue_bounce(&evp, &req_bounce->bounce); - evp.lastbounce = req_bounce->timestamp; - if (!queue_envelope_update(&evp)) - log_warnx("warn: could not update envelope %016"PRIx64, evpid); - return; - - case IMSG_SCHED_ENVELOPE_DELIVER: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: deliver: failed to load envelope"); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_add_u32(p_scheduler, 1); /* in-flight */ - m_close(p_scheduler); - return; - } - evp.lasttry = time(NULL); - m_create(p_pony, IMSG_QUEUE_DELIVER, 0, 0, -1); - m_add_envelope(p_pony, &evp); - m_close(p_pony); - return; - - case IMSG_SCHED_ENVELOPE_INJECT: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - bounce_add(evpid); - return; - - case IMSG_SCHED_ENVELOPE_TRANSFER: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: failed to load envelope"); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_add_u32(p_scheduler, 1); /* in-flight */ - m_close(p_scheduler); - return; - } - evp.lasttry = time(NULL); - m_create(p_pony, IMSG_QUEUE_TRANSFER, 0, 0, -1); - m_add_envelope(p_pony, &evp); - m_close(p_pony); - return; - - case IMSG_CTL_LIST_ENVELOPES: - if (imsg->hdr.len == sizeof imsg->hdr) { - m_forward(p_control, imsg); - return; - } - - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_get_int(&m, &flags); - m_get_time(&m, &nexttry); - m_end(&m); - - if (queue_envelope_load(evpid, &evp) == 0) - return; /* Envelope is gone, drop it */ - - /* - * XXX consistency: The envelope might already be on - * its way back to the scheduler. We need to detect - * this properly and report that state. - */ - if (flags & EF_INFLIGHT) { - /* - * Not exactly correct but pretty close: The - * value is not recorded on the envelope unless - * a tempfail occurs. - */ - evp.lasttry = nexttry; - } - - m_create(p_control, IMSG_CTL_LIST_ENVELOPES, - imsg->hdr.peerid, 0, -1); - m_add_int(p_control, flags); - m_add_time(p_control, nexttry); - m_add_envelope(p_control, &evp); - m_close(p_control); - return; - - case IMSG_MDA_OPEN_MESSAGE: - case IMSG_MTA_OPEN_MESSAGE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_msgid(&m, &msgid); - m_end(&m); - fd = queue_message_fd_r(msgid); - m_create(p, imsg->hdr.type, 0, 0, fd); - m_add_id(p, reqid); - m_close(p); - return; - - case IMSG_MDA_DELIVERY_OK: - case IMSG_MTA_DELIVERY_OK: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK) - m_get_int(&m, &mta_ext); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warn("queue: dsn: failed to load envelope"); - return; - } - if (evp.dsn_notify & DSN_SUCCESS) { - bounce.type = B_DELIVERED; - bounce.dsn_ret = evp.dsn_ret; - envelope_set_esc_class(&evp, ESC_STATUS_OK); - if (imsg->hdr.type == IMSG_MDA_DELIVERY_OK) - queue_bounce(&evp, &bounce); - else if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK && - (mta_ext & MTA_EXT_DSN) == 0) { - bounce.mta_without_dsn = 1; - queue_bounce(&evp, &bounce); - } - } - queue_envelope_delete(evpid); - m_create(p_scheduler, IMSG_QUEUE_DELIVERY_OK, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_close(p_scheduler); - return; - - case IMSG_MDA_DELIVERY_TEMPFAIL: - case IMSG_MTA_DELIVERY_TEMPFAIL: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_get_string(&m, &reason); - m_get_int(&m, &code); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: tempfail: failed to load envelope"); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_add_u32(p_scheduler, 1); /* in-flight */ - m_close(p_scheduler); - return; - } - envelope_set_errormsg(&evp, "%s", reason); - envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); - envelope_set_esc_code(&evp, code); - evp.retry++; - if (!queue_envelope_update(&evp)) - log_warnx("warn: could not update envelope %016"PRIx64, evpid); - m_create(p_scheduler, IMSG_QUEUE_DELIVERY_TEMPFAIL, 0, 0, -1); - m_add_envelope(p_scheduler, &evp); - m_close(p_scheduler); - return; - - case IMSG_MDA_DELIVERY_PERMFAIL: - case IMSG_MTA_DELIVERY_PERMFAIL: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_get_string(&m, &reason); - m_get_int(&m, &code); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: permfail: failed to load envelope"); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_add_u32(p_scheduler, 1); /* in-flight */ - m_close(p_scheduler); - return; - } - bounce.type = B_FAILED; - envelope_set_errormsg(&evp, "%s", reason); - envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL); - envelope_set_esc_code(&evp, code); - queue_bounce(&evp, &bounce); - queue_envelope_delete(evpid); - m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_close(p_scheduler); - return; - - case IMSG_MDA_DELIVERY_LOOP: - case IMSG_MTA_DELIVERY_LOOP: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: loop: failed to load envelope"); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_scheduler, evpid); - m_add_u32(p_scheduler, 1); /* in-flight */ - m_close(p_scheduler); - return; - } - envelope_set_errormsg(&evp, "%s", "Loop detected"); - envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); - envelope_set_esc_code(&evp, ESC_ROUTING_LOOP_DETECTED); - bounce.type = B_FAILED; - queue_bounce(&evp, &bounce); - queue_envelope_delete(evp.id); - m_create(p_scheduler, IMSG_QUEUE_DELIVERY_LOOP, 0, 0, -1); - m_add_evpid(p_scheduler, evp.id); - m_close(p_scheduler); - return; - - case IMSG_MTA_DELIVERY_HOLD: - case IMSG_MDA_DELIVERY_HOLD: - imsg->hdr.type = IMSG_QUEUE_HOLDQ_HOLD; - m_forward(p_scheduler, imsg); - return; - - case IMSG_MTA_SCHEDULE: - imsg->hdr.type = IMSG_QUEUE_ENVELOPE_SCHEDULE; - m_forward(p_scheduler, imsg); - return; - - case IMSG_MTA_HOLDQ_RELEASE: - case IMSG_MDA_HOLDQ_RELEASE: - m_msg(&m, imsg); - m_get_id(&m, &holdq); - m_get_int(&m, &v); - m_end(&m); - m_create(p_scheduler, IMSG_QUEUE_HOLDQ_RELEASE, 0, 0, -1); - if (imsg->hdr.type == IMSG_MTA_HOLDQ_RELEASE) - m_add_int(p_scheduler, D_MTA); - else - m_add_int(p_scheduler, D_MDA); - m_add_id(p_scheduler, holdq); - m_add_int(p_scheduler, v); - m_close(p_scheduler); - return; - - case IMSG_CTL_PAUSE_MDA: - case IMSG_CTL_PAUSE_MTA: - case IMSG_CTL_RESUME_MDA: - case IMSG_CTL_RESUME_MTA: - m_forward(p_scheduler, imsg); - return; - - case IMSG_CTL_VERBOSE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - log_trace_verbose(v); - return; - - case IMSG_CTL_PROFILE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - profiling = v; - return; - - case IMSG_CTL_DISCOVER_EVPID: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - if (queue_envelope_load(evpid, &evp) == 0) { - log_warnx("queue: discover: failed to load " - "envelope %016" PRIx64, evpid); - n_evp = 0; - m_compose(p_control, imsg->hdr.type, - imsg->hdr.peerid, 0, -1, - &n_evp, sizeof n_evp); - return; - } - - m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID, - 0, 0, -1); - m_add_envelope(p_scheduler, &evp); - m_close(p_scheduler); - - m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID, - 0, 0, -1); - m_add_msgid(p_scheduler, evpid_to_msgid(evpid)); - m_close(p_scheduler); - n_evp = 1; - m_compose(p_control, imsg->hdr.type, imsg->hdr.peerid, - 0, -1, &n_evp, sizeof n_evp); - return; - - case IMSG_CTL_DISCOVER_MSGID: - m_msg(&m, imsg); - m_get_msgid(&m, &msgid); - m_end(&m); - /* handle concurrent walk requests */ - wi = xcalloc(1, sizeof *wi); - wi->msgid = msgid; - wi->peerid = imsg->hdr.peerid; - evtimer_set(&wi->ev, queue_msgid_walk, wi); - tv.tv_sec = 0; - tv.tv_usec = 10; - evtimer_add(&wi->ev, &tv); - return; - } - - errx(1, "queue_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -static void -queue_msgid_walk(int fd, short event, void *arg) -{ - struct envelope evp; - struct timeval tv; - struct msg_walkinfo *wi = arg; - int r; - - r = queue_message_walk(&evp, wi->msgid, &wi->done, &wi->data); - if (r == -1) { - if (wi->n_evp) { - m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID, - 0, 0, -1); - m_add_msgid(p_scheduler, wi->msgid); - m_close(p_scheduler); - } - - m_compose(p_control, IMSG_CTL_DISCOVER_MSGID, wi->peerid, 0, -1, - &wi->n_evp, sizeof wi->n_evp); - evtimer_del(&wi->ev); - free(wi); - return; - } - - if (r) { - m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID, 0, 0, -1); - m_add_envelope(p_scheduler, &evp); - m_close(p_scheduler); - wi->n_evp += 1; - } - - tv.tv_sec = 0; - tv.tv_usec = 10; - evtimer_set(&wi->ev, queue_msgid_walk, wi); - evtimer_add(&wi->ev, &tv); -} - -static void -queue_bounce(struct envelope *e, struct delivery_bounce *d) -{ - struct envelope b; - - b = *e; - b.type = D_BOUNCE; - b.agent.bounce = *d; - b.retry = 0; - b.lasttry = 0; - b.creation = time(NULL); - b.ttl = 3600 * 24 * 7; - - if (e->dsn_notify & DSN_NEVER) - return; - - if (b.id == 0) - log_warnx("warn: queue_bounce: evpid=0"); - if (evpid_to_msgid(b.id) == 0) - log_warnx("warn: queue_bounce: msgid=0, evpid=%016"PRIx64, - b.id); - if (e->type == D_BOUNCE) { - log_warnx("warn: queue: double bounce!"); - } else if (e->sender.user[0] == '\0') { - log_warnx("warn: queue: no return path!"); - } else if (!queue_envelope_create(&b)) { - log_warnx("warn: queue: cannot bounce!"); - } else { - log_debug("debug: queue: bouncing evp:%016" PRIx64 - " as evp:%016" PRIx64, e->id, b.id); - - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); - m_add_envelope(p_scheduler, &b); - m_close(p_scheduler); - - m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1); - m_add_msgid(p_scheduler, evpid_to_msgid(b.id)); - m_close(p_scheduler); - - stat_increment("queue.bounce", 1); - } -} - -static void -queue_shutdown(void) -{ - log_debug("debug: queue agent exiting"); - queue_close(); - _exit(0); -} - -int -queue(void) -{ - struct passwd *pw; - struct timeval tv; - struct event ev_qload; - - purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS); - - if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) - if ((pw = getpwnam(SMTPD_USER)) == NULL) - fatalx("unknown user " SMTPD_USER); - - env->sc_queue_flags |= QUEUE_EVPCACHE; - env->sc_queue_evpcache_size = 1024; - - if (chroot(PATH_SPOOL) == -1) - fatal("queue: chroot"); - if (chdir("/") == -1) - fatal("queue: chdir(\"/\")"); - - config_process(PROC_QUEUE); - - if (env->sc_queue_flags & QUEUE_COMPRESSION) - log_info("queue: queue compression enabled"); - - if (env->sc_queue_key) { - if (!crypto_setup(env->sc_queue_key, strlen(env->sc_queue_key))) - fatalx("crypto_setup: invalid key for queue encryption"); - log_info("queue: queue encryption enabled"); - } - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("queue: cannot drop privileges"); - - imsg_callback = queue_imsg; - event_init(); - - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - - config_peer(PROC_PARENT); - config_peer(PROC_CONTROL); - config_peer(PROC_LKA); - config_peer(PROC_SCHEDULER); - config_peer(PROC_PONY); - - /* setup queue loading task */ - evtimer_set(&ev_qload, queue_timeout, &ev_qload); - tv.tv_sec = 0; - tv.tv_usec = 10; - evtimer_add(&ev_qload, &tv); - -#if HAVE_PLEDGE - if (pledge("stdio rpath wpath cpath flock recvfd sendfd", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} - -static void -queue_timeout(int fd, short event, void *p) -{ - static uint32_t msgid = 0; - struct envelope evp; - struct event *ev = p; - struct timeval tv; - int r; - - r = queue_envelope_walk(&evp); - if (r == -1) { - if (msgid) { - m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, - 0, 0, -1); - m_add_msgid(p_scheduler, msgid); - m_close(p_scheduler); - } - log_debug("debug: queue: done loading queue into scheduler"); - return; - } - - if (r) { - if (msgid && evpid_to_msgid(evp.id) != msgid) { - m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, - 0, 0, -1); - m_add_msgid(p_scheduler, msgid); - m_close(p_scheduler); - } - msgid = evpid_to_msgid(evp.id); - m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); - m_add_envelope(p_scheduler, &evp); - m_close(p_scheduler); - } - - tv.tv_sec = 0; - tv.tv_usec = 10; - evtimer_add(ev, &tv); -} - -static void -queue_log(const struct envelope *e, const char *prefix, const char *status) -{ - char rcpt[LINE_MAX]; - - (void)strlcpy(rcpt, "-", sizeof rcpt); - if (strcmp(e->rcpt.user, e->dest.user) || - strcmp(e->rcpt.domain, e->dest.domain)) - (void)snprintf(rcpt, sizeof rcpt, "%s@%s", - e->rcpt.user, e->rcpt.domain); - - log_info("%s: %s for %016" PRIx64 ": from=<%s@%s>, to=<%s@%s>, " - "rcpt=<%s>, delay=%s, stat=%s", - e->type == D_MDA ? "delivery" : "relay", - prefix, - e->id, e->sender.user, e->sender.domain, - e->dest.user, e->dest.domain, - rcpt, - duration_to_text(time(NULL) - e->creation), - status); -} diff --git a/smtpd/queue_backend.c b/smtpd/queue_backend.c deleted file mode 100644 index fa945f47..00000000 --- a/smtpd/queue_backend.c +++ /dev/null @@ -1,806 +0,0 @@ -/* $OpenBSD: queue_backend.c,v 1.66 2020/04/22 11:35:34 eric Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <grp.h> -#include <imsg.h> -#include <limits.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -static const char* envelope_validate(struct envelope *); - -extern struct queue_backend queue_backend_fs; -extern struct queue_backend queue_backend_null; -extern struct queue_backend queue_backend_proc; -extern struct queue_backend queue_backend_ram; - -static void queue_envelope_cache_add(struct envelope *); -static void queue_envelope_cache_update(struct envelope *); -static void queue_envelope_cache_del(uint64_t evpid); - -TAILQ_HEAD(evplst, envelope); - -static struct tree evpcache_tree; -static struct evplst evpcache_list; -static struct queue_backend *backend; - -static int (*handler_close)(void); -static int (*handler_message_create)(uint32_t *); -static int (*handler_message_commit)(uint32_t, const char*); -static int (*handler_message_delete)(uint32_t); -static int (*handler_message_fd_r)(uint32_t); -static int (*handler_envelope_create)(uint32_t, const char *, size_t, uint64_t *); -static int (*handler_envelope_delete)(uint64_t); -static int (*handler_envelope_update)(uint64_t, const char *, size_t); -static int (*handler_envelope_load)(uint64_t, char *, size_t); -static int (*handler_envelope_walk)(uint64_t *, char *, size_t); -static int (*handler_message_walk)(uint64_t *, char *, size_t, - uint32_t, int *, void **); - -#ifdef QUEUE_PROFILING - -static struct { - struct timespec t0; - const char *name; -} profile; - -static inline void profile_enter(const char *name) -{ - if ((profiling & PROFILE_QUEUE) == 0) - return; - - profile.name = name; - clock_gettime(CLOCK_MONOTONIC, &profile.t0); -} - -static inline void profile_leave(void) -{ - struct timespec t1, dt; - - if ((profiling & PROFILE_QUEUE) == 0) - return; - - clock_gettime(CLOCK_MONOTONIC, &t1); - timespecsub(&t1, &profile.t0, &dt); - log_debug("profile-queue: %s %lld.%09ld", profile.name, - (long long)dt.tv_sec, dt.tv_nsec); -} -#else -#define profile_enter(x) do {} while (0) -#define profile_leave() do {} while (0) -#endif - -static int -queue_message_path(uint32_t msgid, char *buf, size_t len) -{ - return bsnprintf(buf, len, "%s/%08"PRIx32, PATH_TEMPORARY, msgid); -} - -int -queue_init(const char *name, int server) -{ - struct passwd *pwq; - struct group *gr; - int r; - - pwq = getpwnam(SMTPD_QUEUE_USER); - if (pwq == NULL) - errx(1, "unknown user %s", SMTPD_QUEUE_USER); - - gr = getgrnam(SMTPD_QUEUE_GROUP); - if (gr == NULL) - errx(1, "unknown group %s", SMTPD_QUEUE_GROUP); - - tree_init(&evpcache_tree); - TAILQ_INIT(&evpcache_list); - - if (!strcmp(name, "fs")) - backend = &queue_backend_fs; - else if (!strcmp(name, "null")) - backend = &queue_backend_null; - else if (!strcmp(name, "ram")) - backend = &queue_backend_ram; - else - backend = &queue_backend_proc; - - if (server) { - if (ckdir(PATH_SPOOL, 0711, 0, 0, 1) == 0) - errx(1, "error in spool directory setup"); - if (ckdir(PATH_SPOOL PATH_OFFLINE, 0770, 0, gr->gr_gid, 1) == 0) - errx(1, "error in offline directory setup"); - if (ckdir(PATH_SPOOL PATH_PURGE, 0700, pwq->pw_uid, 0, 1) == 0) - errx(1, "error in purge directory setup"); - - mvpurge(PATH_SPOOL PATH_TEMPORARY, PATH_SPOOL PATH_PURGE); - - if (ckdir(PATH_SPOOL PATH_TEMPORARY, 0700, pwq->pw_uid, 0, 1) == 0) - errx(1, "error in purge directory setup"); - } - - r = backend->init(pwq, server, name); - - log_trace(TRACE_QUEUE, "queue-backend: queue_init(%d) -> %d", server, r); - - return (r); -} - -int -queue_close(void) -{ - if (handler_close) - return (handler_close()); - - return (1); -} - -int -queue_message_create(uint32_t *msgid) -{ - int r; - - profile_enter("queue_message_create"); - r = handler_message_create(msgid); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_message_create() -> %d (%08"PRIx32")", - r, *msgid); - - return (r); -} - -int -queue_message_delete(uint32_t msgid) -{ - char msgpath[PATH_MAX]; - uint64_t evpid; - void *iter; - int r; - - profile_enter("queue_message_delete"); - r = handler_message_delete(msgid); - profile_leave(); - - /* in case the message is incoming */ - queue_message_path(msgid, msgpath, sizeof(msgpath)); - unlink(msgpath); - - /* remove remaining envelopes from the cache if any (on rollback) */ - evpid = msgid_to_evpid(msgid); - for (;;) { - iter = NULL; - if (!tree_iterfrom(&evpcache_tree, &iter, evpid, &evpid, NULL)) - break; - if (evpid_to_msgid(evpid) != msgid) - break; - queue_envelope_cache_del(evpid); - } - - log_trace(TRACE_QUEUE, - "queue-backend: queue_message_delete(%08"PRIx32") -> %d", msgid, r); - - return (r); -} - -int -queue_message_commit(uint32_t msgid) -{ - int r; - char msgpath[PATH_MAX]; - char tmppath[PATH_MAX]; - FILE *ifp = NULL; - FILE *ofp = NULL; - - profile_enter("queue_message_commit"); - - queue_message_path(msgid, msgpath, sizeof(msgpath)); - - if (env->sc_queue_flags & QUEUE_COMPRESSION) { - bsnprintf(tmppath, sizeof tmppath, "%s.comp", msgpath); - ifp = fopen(msgpath, "r"); - ofp = fopen(tmppath, "w+"); - if (ifp == NULL || ofp == NULL) - goto err; - if (!compress_file(ifp, ofp)) - goto err; - fclose(ifp); - fclose(ofp); - ifp = NULL; - ofp = NULL; - - if (rename(tmppath, msgpath) == -1) { - if (errno == ENOSPC) - return (0); - unlink(tmppath); - log_warn("rename"); - return (0); - } - } - - if (env->sc_queue_flags & QUEUE_ENCRYPTION) { - bsnprintf(tmppath, sizeof tmppath, "%s.enc", msgpath); - ifp = fopen(msgpath, "r"); - ofp = fopen(tmppath, "w+"); - if (ifp == NULL || ofp == NULL) - goto err; - if (!crypto_encrypt_file(ifp, ofp)) - goto err; - fclose(ifp); - fclose(ofp); - ifp = NULL; - ofp = NULL; - - if (rename(tmppath, msgpath) == -1) { - if (errno == ENOSPC) - return (0); - unlink(tmppath); - log_warn("rename"); - return (0); - } - } - - r = handler_message_commit(msgid, msgpath); - profile_leave(); - - /* in case it's not done by the backend */ - unlink(msgpath); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_message_commit(%08"PRIx32") -> %d", - msgid, r); - - return (r); - -err: - if (ifp) - fclose(ifp); - if (ofp) - fclose(ofp); - return 0; -} - -int -queue_message_fd_r(uint32_t msgid) -{ - int fdin = -1, fdout = -1, fd = -1; - FILE *ifp = NULL; - FILE *ofp = NULL; - - profile_enter("queue_message_fd_r"); - fdin = handler_message_fd_r(msgid); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_message_fd_r(%08"PRIx32") -> %d", msgid, fdin); - - if (fdin == -1) - return (-1); - - if (env->sc_queue_flags & QUEUE_ENCRYPTION) { - if ((fdout = mktmpfile()) == -1) - goto err; - if ((fd = dup(fdout)) == -1) - goto err; - if ((ifp = fdopen(fdin, "r")) == NULL) - goto err; - fdin = fd; - fd = -1; - if ((ofp = fdopen(fdout, "w+")) == NULL) - goto err; - - if (!crypto_decrypt_file(ifp, ofp)) - goto err; - - fclose(ifp); - ifp = NULL; - fclose(ofp); - ofp = NULL; - lseek(fdin, SEEK_SET, 0); - } - - if (env->sc_queue_flags & QUEUE_COMPRESSION) { - if ((fdout = mktmpfile()) == -1) - goto err; - if ((fd = dup(fdout)) == -1) - goto err; - if ((ifp = fdopen(fdin, "r")) == NULL) - goto err; - fdin = fd; - fd = -1; - if ((ofp = fdopen(fdout, "w+")) == NULL) - goto err; - - if (!uncompress_file(ifp, ofp)) - goto err; - - fclose(ifp); - ifp = NULL; - fclose(ofp); - ofp = NULL; - lseek(fdin, SEEK_SET, 0); - } - - return (fdin); - -err: - if (fd != -1) - close(fd); - if (fdin != -1) - close(fdin); - if (fdout != -1) - close(fdout); - if (ifp) - fclose(ifp); - if (ofp) - fclose(ofp); - return -1; -} - -int -queue_message_fd_rw(uint32_t msgid) -{ - char buf[PATH_MAX]; - - queue_message_path(msgid, buf, sizeof(buf)); - - return open(buf, O_RDWR | O_CREAT | O_EXCL, 0600); -} - -static int -queue_envelope_dump_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize) -{ - char *evp; - size_t evplen; - size_t complen; - char compbuf[sizeof(struct envelope)]; - size_t enclen; - char encbuf[sizeof(struct envelope)]; - - evp = evpbuf; - evplen = envelope_dump_buffer(ep, evpbuf, evpbufsize); - if (evplen == 0) - return (0); - - if (env->sc_queue_flags & QUEUE_COMPRESSION) { - complen = compress_chunk(evp, evplen, compbuf, sizeof compbuf); - if (complen == 0) - return (0); - evp = compbuf; - evplen = complen; - } - - if (env->sc_queue_flags & QUEUE_ENCRYPTION) { - enclen = crypto_encrypt_buffer(evp, evplen, encbuf, sizeof encbuf); - if (enclen == 0) - return (0); - evp = encbuf; - evplen = enclen; - } - - memmove(evpbuf, evp, evplen); - - return (evplen); -} - -static int -queue_envelope_load_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize) -{ - char *evp; - size_t evplen; - char compbuf[sizeof(struct envelope)]; - size_t complen; - char encbuf[sizeof(struct envelope)]; - size_t enclen; - - evp = evpbuf; - evplen = evpbufsize; - - if (env->sc_queue_flags & QUEUE_ENCRYPTION) { - enclen = crypto_decrypt_buffer(evp, evplen, encbuf, sizeof encbuf); - if (enclen == 0) - return (0); - evp = encbuf; - evplen = enclen; - } - - if (env->sc_queue_flags & QUEUE_COMPRESSION) { - complen = uncompress_chunk(evp, evplen, compbuf, sizeof compbuf); - if (complen == 0) - return (0); - evp = compbuf; - evplen = complen; - } - - return (envelope_load_buffer(ep, evp, evplen)); -} - -static void -queue_envelope_cache_add(struct envelope *e) -{ - struct envelope *cached; - - while (tree_count(&evpcache_tree) >= env->sc_queue_evpcache_size) - queue_envelope_cache_del(TAILQ_LAST(&evpcache_list, evplst)->id); - - cached = xcalloc(1, sizeof *cached); - *cached = *e; - TAILQ_INSERT_HEAD(&evpcache_list, cached, entry); - tree_xset(&evpcache_tree, e->id, cached); - stat_increment("queue.evpcache.size", 1); -} - -static void -queue_envelope_cache_update(struct envelope *e) -{ - struct envelope *cached; - - if ((cached = tree_get(&evpcache_tree, e->id)) == NULL) { - queue_envelope_cache_add(e); - stat_increment("queue.evpcache.update.missed", 1); - } else { - TAILQ_REMOVE(&evpcache_list, cached, entry); - *cached = *e; - TAILQ_INSERT_HEAD(&evpcache_list, cached, entry); - stat_increment("queue.evpcache.update.hit", 1); - } -} - -static void -queue_envelope_cache_del(uint64_t evpid) -{ - struct envelope *cached; - - if ((cached = tree_pop(&evpcache_tree, evpid)) == NULL) - return; - - TAILQ_REMOVE(&evpcache_list, cached, entry); - free(cached); - stat_decrement("queue.evpcache.size", 1); -} - -int -queue_envelope_create(struct envelope *ep) -{ - int r; - char evpbuf[sizeof(struct envelope)]; - size_t evplen; - uint64_t evpid; - uint32_t msgid; - - ep->creation = time(NULL); - evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf); - if (evplen == 0) - return (0); - - evpid = ep->id; - msgid = evpid_to_msgid(evpid); - - profile_enter("queue_envelope_create"); - r = handler_envelope_create(msgid, evpbuf, evplen, &ep->id); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_envelope_create(%016"PRIx64", %zu) -> %d (%016"PRIx64")", - evpid, evplen, r, ep->id); - - if (!r) { - ep->creation = 0; - ep->id = 0; - } - - if (r && env->sc_queue_flags & QUEUE_EVPCACHE) - queue_envelope_cache_add(ep); - - return (r); -} - -int -queue_envelope_delete(uint64_t evpid) -{ - int r; - - if (env->sc_queue_flags & QUEUE_EVPCACHE) - queue_envelope_cache_del(evpid); - - profile_enter("queue_envelope_delete"); - r = handler_envelope_delete(evpid); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_envelope_delete(%016"PRIx64") -> %d", - evpid, r); - - return (r); -} - -int -queue_envelope_load(uint64_t evpid, struct envelope *ep) -{ - const char *e; - char evpbuf[sizeof(struct envelope)]; - size_t evplen; - struct envelope *cached; - - if ((env->sc_queue_flags & QUEUE_EVPCACHE) && - (cached = tree_get(&evpcache_tree, evpid))) { - *ep = *cached; - stat_increment("queue.evpcache.load.hit", 1); - return (1); - } - - ep->id = evpid; - profile_enter("queue_envelope_load"); - evplen = handler_envelope_load(ep->id, evpbuf, sizeof evpbuf); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_envelope_load(%016"PRIx64") -> %zu", - evpid, evplen); - - if (evplen == 0) - return (0); - - if (queue_envelope_load_buffer(ep, evpbuf, evplen)) { - if ((e = envelope_validate(ep)) == NULL) { - ep->id = evpid; - if (env->sc_queue_flags & QUEUE_EVPCACHE) { - queue_envelope_cache_add(ep); - stat_increment("queue.evpcache.load.missed", 1); - } - return (1); - } - log_warnx("warn: invalid envelope %016" PRIx64 ": %s", - evpid, e); - } - return (0); -} - -int -queue_envelope_update(struct envelope *ep) -{ - char evpbuf[sizeof(struct envelope)]; - size_t evplen; - int r; - - evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf); - if (evplen == 0) - return (0); - - profile_enter("queue_envelope_update"); - r = handler_envelope_update(ep->id, evpbuf, evplen); - profile_leave(); - - if (r && env->sc_queue_flags & QUEUE_EVPCACHE) - queue_envelope_cache_update(ep); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_envelope_update(%016"PRIx64") -> %d", - ep->id, r); - - return (r); -} - -int -queue_message_walk(struct envelope *ep, uint32_t msgid, int *done, void **data) -{ - char evpbuf[sizeof(struct envelope)]; - uint64_t evpid; - int r; - const char *e; - - profile_enter("queue_message_walk"); - r = handler_message_walk(&evpid, evpbuf, sizeof evpbuf, - msgid, done, data); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_message_walk() -> %d (%016"PRIx64")", - r, evpid); - - if (r == -1) - return (r); - - if (r && queue_envelope_load_buffer(ep, evpbuf, (size_t)r)) { - if ((e = envelope_validate(ep)) == NULL) { - ep->id = evpid; - /* - * do not cache the envelope here, while discovering - * envelopes one could re-run discover on already - * scheduled envelopes which leads to triggering of - * strict checks in caching. Envelopes could anyway - * be loaded from backend if it isn't cached. - */ - return (1); - } - log_warnx("warn: invalid envelope %016" PRIx64 ": %s", - evpid, e); - } - return (0); -} - -int -queue_envelope_walk(struct envelope *ep) -{ - const char *e; - uint64_t evpid; - char evpbuf[sizeof(struct envelope)]; - int r; - - profile_enter("queue_envelope_walk"); - r = handler_envelope_walk(&evpid, evpbuf, sizeof evpbuf); - profile_leave(); - - log_trace(TRACE_QUEUE, - "queue-backend: queue_envelope_walk() -> %d (%016"PRIx64")", - r, evpid); - - if (r == -1) - return (r); - - if (r && queue_envelope_load_buffer(ep, evpbuf, (size_t)r)) { - if ((e = envelope_validate(ep)) == NULL) { - ep->id = evpid; - if (env->sc_queue_flags & QUEUE_EVPCACHE) - queue_envelope_cache_add(ep); - return (1); - } - log_warnx("warn: invalid envelope %016" PRIx64 ": %s", - evpid, e); - } - return (0); -} - -uint32_t -queue_generate_msgid(void) -{ - uint32_t msgid; - - while ((msgid = arc4random()) == 0) - ; - - return msgid; -} - -uint64_t -queue_generate_evpid(uint32_t msgid) -{ - uint32_t rnd; - uint64_t evpid; - - while ((rnd = arc4random()) == 0) - ; - - evpid = msgid; - evpid <<= 32; - evpid |= rnd; - - return evpid; -} - -static const char* -envelope_validate(struct envelope *ep) -{ - if (ep->version != SMTPD_ENVELOPE_VERSION) - return "version mismatch"; - - if (memchr(ep->helo, '\0', sizeof(ep->helo)) == NULL) - return "invalid helo"; - if (ep->helo[0] == '\0') - return "empty helo"; - - if (memchr(ep->hostname, '\0', sizeof(ep->hostname)) == NULL) - return "invalid hostname"; - if (ep->hostname[0] == '\0') - return "empty hostname"; - - if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL) - return "invalid error line"; - - if (dict_get(env->sc_dispatchers, ep->dispatcher) == NULL) - return "unknown dispatcher"; - - return NULL; -} - -void -queue_api_on_close(int(*cb)(void)) -{ - handler_close = cb; -} - -void -queue_api_on_message_create(int(*cb)(uint32_t *)) -{ - handler_message_create = cb; -} - -void -queue_api_on_message_commit(int(*cb)(uint32_t, const char *)) -{ - handler_message_commit = cb; -} - -void -queue_api_on_message_delete(int(*cb)(uint32_t)) -{ - handler_message_delete = cb; -} - -void -queue_api_on_message_fd_r(int(*cb)(uint32_t)) -{ - handler_message_fd_r = cb; -} - -void -queue_api_on_envelope_create(int(*cb)(uint32_t, const char *, size_t, uint64_t *)) -{ - handler_envelope_create = cb; -} - -void -queue_api_on_envelope_delete(int(*cb)(uint64_t)) -{ - handler_envelope_delete = cb; -} - -void -queue_api_on_envelope_update(int(*cb)(uint64_t, const char *, size_t)) -{ - handler_envelope_update = cb; -} - -void -queue_api_on_envelope_load(int(*cb)(uint64_t, char *, size_t)) -{ - handler_envelope_load = cb; -} - -void -queue_api_on_envelope_walk(int(*cb)(uint64_t *, char *, size_t)) -{ - handler_envelope_walk = cb; -} - -void -queue_api_on_message_walk(int(*cb)(uint64_t *, char *, size_t, - uint32_t, int *, void **)) -{ - handler_message_walk = cb; -} diff --git a/smtpd/queue_fs.c b/smtpd/queue_fs.c deleted file mode 100644 index 097ba1e2..00000000 --- a/smtpd/queue_fs.c +++ /dev/null @@ -1,695 +0,0 @@ -/* $OpenBSD: queue_fs.c,v 1.20 2020/02/25 17:03:13 millert Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#if HAVE_SYS_MOUNT_H -#include <sys/mount.h> -#endif -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#ifdef HAVE_SYS_STATFS_H -#include <sys/statfs.h> -#endif - -#include <ctype.h> -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <fts.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -#define PATH_QUEUE "/queue" -#define PATH_INCOMING "/incoming" -#define PATH_EVPTMP PATH_INCOMING "/envelope.tmp" -#define PATH_MESSAGE "/message" - -/* percentage of remaining space / inodes required to accept new messages */ -#define MINSPACE 5 -#define MININODES 5 - -struct qwalk { - FTS *fts; - int depth; -}; - -static int fsqueue_check_space(void); -static void fsqueue_envelope_path(uint64_t, char *, size_t); -static void fsqueue_envelope_incoming_path(uint64_t, char *, size_t); -static int fsqueue_envelope_dump(char *, const char *, size_t, int, int); -static void fsqueue_message_path(uint32_t, char *, size_t); -static void fsqueue_message_incoming_path(uint32_t, char *, size_t); -static void *fsqueue_qwalk_new(void); -static int fsqueue_qwalk(void *, uint64_t *); -static void fsqueue_qwalk_close(void *); - -struct tree evpcount; -static struct timespec startup; - -#define REF (int*)0xf00 - -static int -queue_fs_message_create(uint32_t *msgid) -{ - char rootdir[PATH_MAX]; - struct stat sb; - - if (!fsqueue_check_space()) - return 0; - -again: - *msgid = queue_generate_msgid(); - - /* prevent possible collision later when moving to Q_QUEUE */ - fsqueue_message_path(*msgid, rootdir, sizeof(rootdir)); - if (stat(rootdir, &sb) != -1) - goto again; - - /* we hit an unexpected error, temporarily fail */ - if (errno != ENOENT) { - *msgid = 0; - return 0; - } - - fsqueue_message_incoming_path(*msgid, rootdir, sizeof(rootdir)); - if (mkdir(rootdir, 0700) == -1) { - if (errno == EEXIST) - goto again; - - if (errno == ENOSPC) { - *msgid = 0; - return 0; - } - - log_warn("warn: queue-fs: mkdir"); - *msgid = 0; - return 0; - } - - return (1); -} - -static int -queue_fs_message_commit(uint32_t msgid, const char *path) -{ - char incomingdir[PATH_MAX]; - char queuedir[PATH_MAX]; - char msgdir[PATH_MAX]; - char msgpath[PATH_MAX]; - - /* before-first, move the message content in the incoming directory */ - fsqueue_message_incoming_path(msgid, msgpath, sizeof(msgpath)); - if (strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath)) - >= sizeof(msgpath)) - return (0); - if (rename(path, msgpath) == -1) - return (0); - - fsqueue_message_incoming_path(msgid, incomingdir, sizeof(incomingdir)); - fsqueue_message_path(msgid, msgdir, sizeof(msgdir)); - if (strlcpy(queuedir, msgdir, sizeof(queuedir)) - >= sizeof(queuedir)) - return (0); - - /* first attempt to rename */ - if (rename(incomingdir, msgdir) == 0) - return 1; - if (errno == ENOSPC) - return 0; - if (errno != ENOENT) { - log_warn("warn: queue-fs: rename"); - return 0; - } - - /* create the bucket */ - *strrchr(queuedir, '/') = '\0'; - if (mkdir(queuedir, 0700) == -1) { - if (errno == ENOSPC) - return 0; - if (errno != EEXIST) { - log_warn("warn: queue-fs: mkdir"); - return 0; - } - } - - /* rename */ - if (rename(incomingdir, msgdir) == -1) { - if (errno == ENOSPC) - return 0; - log_warn("warn: queue-fs: rename"); - return 0; - } - - return 1; -} - -static int -queue_fs_message_fd_r(uint32_t msgid) -{ - int fd; - char path[PATH_MAX]; - - fsqueue_message_path(msgid, path, sizeof(path)); - if (strlcat(path, PATH_MESSAGE, sizeof(path)) - >= sizeof(path)) - return -1; - - if ((fd = open(path, O_RDONLY)) == -1) { - log_warn("warn: queue-fs: open"); - return -1; - } - - return fd; -} - -static int -queue_fs_message_delete(uint32_t msgid) -{ - char path[PATH_MAX]; - struct stat sb; - - fsqueue_message_incoming_path(msgid, path, sizeof(path)); - if (stat(path, &sb) == -1) - fsqueue_message_path(msgid, path, sizeof(path)); - - if (rmtree(path, 0) == -1) - log_warn("warn: queue-fs: rmtree"); - - tree_pop(&evpcount, msgid); - - return 1; -} - -static int -queue_fs_envelope_create(uint32_t msgid, const char *buf, size_t len, - uint64_t *evpid) -{ - char path[PATH_MAX]; - int queued = 0, i, r = 0, *n; - struct stat sb; - - if (msgid == 0) { - log_warnx("warn: queue-fs: msgid=0, evpid=%016"PRIx64, *evpid); - goto done; - } - - fsqueue_message_incoming_path(msgid, path, sizeof(path)); - if (stat(path, &sb) == -1) - queued = 1; - - for (i = 0; i < 20; i ++) { - *evpid = queue_generate_evpid(msgid); - if (queued) - fsqueue_envelope_path(*evpid, path, sizeof(path)); - else - fsqueue_envelope_incoming_path(*evpid, path, - sizeof(path)); - - if ((r = fsqueue_envelope_dump(path, buf, len, 0, 0)) != 0) - goto done; - } - r = 0; - log_warnx("warn: queue-fs: could not allocate evpid"); - -done: - if (r) { - n = tree_pop(&evpcount, msgid); - if (n == NULL) - n = REF; - n += 1; - tree_xset(&evpcount, msgid, n); - } - return (r); -} - -static int -queue_fs_envelope_load(uint64_t evpid, char *buf, size_t len) -{ - char pathname[PATH_MAX]; - FILE *fp; - size_t r; - - fsqueue_envelope_path(evpid, pathname, sizeof(pathname)); - - fp = fopen(pathname, "r"); - if (fp == NULL) { - if (errno != ENOENT && errno != ENFILE) - log_warn("warn: queue-fs: fopen"); - return 0; - } - - r = fread(buf, 1, len, fp); - if (r) { - if (r == len) { - log_warn("warn: queue-fs: too large"); - r = 0; - } - else - buf[r] = '\0'; - } - fclose(fp); - - return (r); -} - -static int -queue_fs_envelope_update(uint64_t evpid, const char *buf, size_t len) -{ - char dest[PATH_MAX]; - - fsqueue_envelope_path(evpid, dest, sizeof(dest)); - - return (fsqueue_envelope_dump(dest, buf, len, 1, 1)); -} - -static int -queue_fs_envelope_delete(uint64_t evpid) -{ - char pathname[PATH_MAX]; - uint32_t msgid; - int *n; - - fsqueue_envelope_path(evpid, pathname, sizeof(pathname)); - if (unlink(pathname) == -1) - if (errno != ENOENT) - return 0; - - msgid = evpid_to_msgid(evpid); - n = tree_pop(&evpcount, msgid); - n -= 1; - - if (n - REF == 0) - queue_fs_message_delete(msgid); - else - tree_xset(&evpcount, msgid, n); - - return (1); -} - -static int -queue_fs_message_walk(uint64_t *evpid, char *buf, size_t len, - uint32_t msgid, int *done, void **data) -{ - struct dirent *dp; - DIR *dir = *data; - char path[PATH_MAX]; - char msgid_str[9]; - char *tmp; - int r, *n; - - if (*done) - return (-1); - - if (!bsnprintf(path, sizeof path, "%s/%02x/%08x", - PATH_QUEUE, (msgid & 0xff000000) >> 24, msgid)) - fatalx("queue_fs_message_walk: path does not fit buffer"); - - if (dir == NULL) { - if ((dir = opendir(path)) == NULL) { - log_warn("warn: queue_fs: opendir: %s", path); - *done = 1; - return (-1); - } - - *data = dir; - } - - (void)snprintf(msgid_str, sizeof msgid_str, "%08" PRIx32, msgid); - while ((dp = readdir(dir)) != NULL) { -#if defined(HAVE_STRUCT_DIR_D_TYPE) - if (dp->d_type != DT_REG) - continue; -#endif - - /* ignore files other than envelopes */ - if (strlen(dp->d_name) != 16 || - strncmp(dp->d_name, msgid_str, 8)) - continue; - - tmp = NULL; - *evpid = strtoull(dp->d_name, &tmp, 16); - if (tmp && *tmp != '\0') { - log_debug("debug: fsqueue: bogus file %s", dp->d_name); - continue; - } - - memset(buf, 0, len); - r = queue_fs_envelope_load(*evpid, buf, len); - if (r) { - n = tree_pop(&evpcount, msgid); - if (n == NULL) - n = REF; - - n += 1; - tree_xset(&evpcount, msgid, n); - } - - return (r); - } - - (void)closedir(dir); - *done = 1; - return (-1); -} - -static int -queue_fs_envelope_walk(uint64_t *evpid, char *buf, size_t len) -{ - static int done = 0; - static void *hdl = NULL; - int r, *n; - uint32_t msgid; - - if (done) - return (-1); - - if (hdl == NULL) - hdl = fsqueue_qwalk_new(); - - if (fsqueue_qwalk(hdl, evpid)) { - memset(buf, 0, len); - r = queue_fs_envelope_load(*evpid, buf, len); - if (r) { - msgid = evpid_to_msgid(*evpid); - n = tree_pop(&evpcount, msgid); - if (n == NULL) - n = REF; - n += 1; - tree_xset(&evpcount, msgid, n); - } - return (r); - } - - fsqueue_qwalk_close(hdl); - done = 1; - return (-1); -} - -static int -fsqueue_check_space(void) -{ -#ifdef __OpenBSD__ - struct statfs buf; - uint64_t used; - uint64_t total; - - if (statfs(PATH_QUEUE, &buf) == -1) { - log_warn("warn: queue-fs: statfs"); - return 0; - } - - /* - * f_bfree and f_ffree is not set on all filesystems. - * They could be signed or unsigned integers. - * Some systems will set them to 0, others will set them to -1. - */ - if (buf.f_bfree == 0 || buf.f_ffree == 0 || - (int64_t)buf.f_bfree == -1 || (int64_t)buf.f_ffree == -1) - return 1; - - used = buf.f_blocks - buf.f_bfree; - total = buf.f_bavail + used; - if (total != 0) - used = (float)used / (float)total * 100; - else - used = 100; - if (100 - used < MINSPACE) { - log_warnx("warn: not enough disk space: %llu%% left", - (unsigned long long) 100 - used); - log_warnx("warn: temporarily rejecting messages"); - return 0; - } - - used = buf.f_files - buf.f_ffree; - total = buf.f_favail + used; - if (total != 0) - used = (float)used / (float)total * 100; - else - used = 100; - if (100 - used < MININODES) { - log_warnx("warn: not enough inodes: %llu%% left", - (unsigned long long) 100 - used); - log_warnx("warn: temporarily rejecting messages"); - return 0; - } -#endif - return 1; -} - -static void -fsqueue_envelope_path(uint64_t evpid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%02x/%08x/%016" PRIx64, - PATH_QUEUE, - (evpid_to_msgid(evpid) & 0xff000000) >> 24, - evpid_to_msgid(evpid), - evpid)) - fatalx("fsqueue_envelope_path: path does not fit buffer"); -} - -static void -fsqueue_envelope_incoming_path(uint64_t evpid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%08x/%016" PRIx64, - PATH_INCOMING, - evpid_to_msgid(evpid), - evpid)) - fatalx("fsqueue_envelope_incoming_path: path does not fit buffer"); -} - -static int -fsqueue_envelope_dump(char *dest, const char *evpbuf, size_t evplen, - int do_atomic, int do_sync) -{ - const char *path = do_atomic ? PATH_EVPTMP : dest; - FILE *fp = NULL; - int fd; - size_t w; - - if ((fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) { - log_warn("warn: queue-fs: open"); - goto tempfail; - } - - if ((fp = fdopen(fd, "w")) == NULL) { - log_warn("warn: queue-fs: fdopen"); - goto tempfail; - } - - w = fwrite(evpbuf, 1, evplen, fp); - if (w < evplen) { - log_warn("warn: queue-fs: short write"); - goto tempfail; - } - if (fflush(fp)) { - log_warn("warn: queue-fs: fflush"); - goto tempfail; - } - if (do_sync && fsync(fileno(fp))) { - log_warn("warn: queue-fs: fsync"); - goto tempfail; - } - if (fclose(fp) != 0) { - log_warn("warn: queue-fs: fclose"); - fp = NULL; - goto tempfail; - } - fp = NULL; - fd = -1; - - if (do_atomic && rename(path, dest) == -1) { - log_warn("warn: queue-fs: rename"); - goto tempfail; - } - return (1); - -tempfail: - if (fp) - fclose(fp); - else if (fd != -1) - close(fd); - if (unlink(path) == -1) - log_warn("warn: queue-fs: unlink"); - return (0); -} - -static void -fsqueue_message_path(uint32_t msgid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%02x/%08x", - PATH_QUEUE, - (msgid & 0xff000000) >> 24, - msgid)) - fatalx("fsqueue_message_path: path does not fit buffer"); -} - -static void -fsqueue_message_incoming_path(uint32_t msgid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%08x", - PATH_INCOMING, - msgid)) - fatalx("fsqueue_message_incoming_path: path does not fit buffer"); -} - -static void * -fsqueue_qwalk_new(void) -{ - char path[PATH_MAX]; - char * const path_argv[] = { path, NULL }; - struct qwalk *q; - - q = xcalloc(1, sizeof(*q)); - (void)strlcpy(path, PATH_QUEUE, sizeof(path)); - q->fts = fts_open(path_argv, - FTS_PHYSICAL | FTS_NOCHDIR, NULL); - - if (q->fts == NULL) - err(1, "fsqueue_qwalk_new: fts_open: %s", path); - - return (q); -} - -static void -fsqueue_qwalk_close(void *hdl) -{ - struct qwalk *q = hdl; - - fts_close(q->fts); - - free(q); -} - -static int -fsqueue_qwalk(void *hdl, uint64_t *evpid) -{ - struct qwalk *q = hdl; - FTSENT *e; - char *tmp; - - while ((e = fts_read(q->fts)) != NULL) { - switch (e->fts_info) { - case FTS_D: - q->depth += 1; - if (q->depth == 2 && e->fts_namelen != 2) { - log_debug("debug: fsqueue: bogus directory %s", - e->fts_path); - fts_set(q->fts, e, FTS_SKIP); - break; - } - if (q->depth == 3 && e->fts_namelen != 8) { - log_debug("debug: fsqueue: bogus directory %s", - e->fts_path); - fts_set(q->fts, e, FTS_SKIP); - break; - } - break; - - case FTS_DP: - case FTS_DNR: - q->depth -= 1; - break; - - case FTS_F: - if (q->depth != 3) - break; - if (e->fts_namelen != 16) - break; -#if HAVE_STRUCT_STAT_ST_MTIM - if (timespeccmp(&e->fts_statp->st_mtim, &startup, >)) -#endif -#if HAVE_STRUCT_STAT_ST_MTIMSPEC - if (timespeccmp(&e->fts_statp->st_mtimspec, &startup, >)) -#endif - break; - tmp = NULL; - *evpid = strtoull(e->fts_name, &tmp, 16); - if (tmp && *tmp != '\0') { - log_debug("debug: fsqueue: bogus file %s", - e->fts_path); - break; - } - return (1); - default: - break; - } - } - - return (0); -} - -static int -queue_fs_init(struct passwd *pw, int server, const char *conf) -{ - unsigned int n; - char *paths[] = { PATH_QUEUE, PATH_INCOMING }; - char path[PATH_MAX]; - int ret; - - /* remove incoming/ if it exists */ - if (server) - mvpurge(PATH_SPOOL PATH_INCOMING, PATH_SPOOL PATH_PURGE); - - fsqueue_envelope_path(0, path, sizeof(path)); - - ret = 1; - for (n = 0; n < nitems(paths); n++) { - (void)strlcpy(path, PATH_SPOOL, sizeof(path)); - if (strlcat(path, paths[n], sizeof(path)) >= sizeof(path)) - errx(1, "path too long %s%s", PATH_SPOOL, paths[n]); - if (ckdir(path, 0700, pw->pw_uid, 0, server) == 0) - ret = 0; - } - - if (clock_gettime(CLOCK_REALTIME, &startup)) - err(1, "clock_gettime"); - - tree_init(&evpcount); - - queue_api_on_message_create(queue_fs_message_create); - queue_api_on_message_commit(queue_fs_message_commit); - queue_api_on_message_delete(queue_fs_message_delete); - queue_api_on_message_fd_r(queue_fs_message_fd_r); - queue_api_on_envelope_create(queue_fs_envelope_create); - queue_api_on_envelope_delete(queue_fs_envelope_delete); - queue_api_on_envelope_update(queue_fs_envelope_update); - queue_api_on_envelope_load(queue_fs_envelope_load); - queue_api_on_envelope_walk(queue_fs_envelope_walk); - queue_api_on_message_walk(queue_fs_message_walk); - - return (ret); -} - -struct queue_backend queue_backend_fs = { - queue_fs_init, -}; diff --git a/smtpd/queue_null.c b/smtpd/queue_null.c deleted file mode 100644 index 1e608be8..00000000 --- a/smtpd/queue_null.c +++ /dev/null @@ -1,120 +0,0 @@ -/* $OpenBSD: queue_null.c,v 1.8 2018/12/30 23:09:58 guenther Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -static int -queue_null_message_create(uint32_t *msgid) -{ - *msgid = queue_generate_msgid(); - return (1); -} - -static int -queue_null_message_commit(uint32_t msgid, const char *path) -{ - return (1); -} - -static int -queue_null_message_delete(uint32_t msgid) -{ - return (1); -} - -static int -queue_null_message_fd_r(uint32_t msgid) -{ - return (-1); -} - -static int -queue_null_envelope_create(uint32_t msgid, const char *buf, size_t len, - uint64_t *evpid) -{ - *evpid = queue_generate_evpid(msgid); - return (1); -} - -static int -queue_null_envelope_delete(uint64_t evpid) -{ - return (1); -} - -static int -queue_null_envelope_update(uint64_t evpid, const char *buf, size_t len) -{ - return (1); -} - -static int -queue_null_envelope_load(uint64_t evpid, char *buf, size_t len) -{ - return (0); -} - -static int -queue_null_envelope_walk(uint64_t *evpid, char *buf, size_t len) -{ - return (-1); -} - -static int -queue_null_init(struct passwd *pw, int server, const char *conf) -{ - queue_api_on_message_create(queue_null_message_create); - queue_api_on_message_commit(queue_null_message_commit); - queue_api_on_message_delete(queue_null_message_delete); - queue_api_on_message_fd_r(queue_null_message_fd_r); - queue_api_on_envelope_create(queue_null_envelope_create); - queue_api_on_envelope_delete(queue_null_envelope_delete); - queue_api_on_envelope_update(queue_null_envelope_update); - queue_api_on_envelope_load(queue_null_envelope_load); - queue_api_on_envelope_walk(queue_null_envelope_walk); - - return (1); -} - -struct queue_backend queue_backend_null = { - queue_null_init, -}; diff --git a/smtpd/queue_proc.c b/smtpd/queue_proc.c deleted file mode 100644 index d6e0f409..00000000 --- a/smtpd/queue_proc.c +++ /dev/null @@ -1,337 +0,0 @@ -/* $OpenBSD: queue_proc.c,v 1.8 2018/12/30 23:09:58 guenther Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -static struct imsgbuf ibuf; -static struct imsg imsg; -static size_t rlen; -static char *rdata; - -static void -queue_proc_call(void) -{ - ssize_t n; - - if (imsg_flush(&ibuf) == -1) { - log_warn("warn: queue-proc: imsg_flush"); - fatalx("queue-proc: exiting"); - } - - while (1) { - if ((n = imsg_get(&ibuf, &imsg)) == -1) { - log_warn("warn: queue-proc: imsg_get"); - break; - } - if (n) { - rlen = imsg.hdr.len - IMSG_HEADER_SIZE; - rdata = imsg.data; - - if (imsg.hdr.type != PROC_QUEUE_OK) { - log_warnx("warn: queue-proc: bad response"); - break; - } - return; - } - - if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN) { - log_warn("warn: queue-proc: imsg_read"); - break; - } - - if (n == 0) { - log_warnx("warn: queue-proc: pipe closed"); - break; - } - } - - fatalx("queue-proc: exiting"); -} - -static void -queue_proc_read(void *dst, size_t len) -{ - if (len > rlen) { - log_warnx("warn: queue-proc: bad msg len"); - fatalx("queue-proc: exiting"); - } - - memmove(dst, rdata, len); - rlen -= len; - rdata += len; -} - -static void -queue_proc_end(void) -{ - if (rlen) { - log_warnx("warn: queue-proc: bogus data"); - fatalx("queue-proc: exiting"); - } - imsg_free(&imsg); -} - -/* - * API - */ - -static int -queue_proc_close(void) -{ - int r; - - imsg_compose(&ibuf, PROC_QUEUE_CLOSE, 0, 0, -1, NULL, 0); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_message_create(uint32_t *msgid) -{ - int r; - - imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_CREATE, 0, 0, -1, NULL, 0); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - if (r == 1) - queue_proc_read(msgid, sizeof(*msgid)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_message_commit(uint32_t msgid, const char *path) -{ - int r, fd; - - fd = open(path, O_RDONLY); - if (fd == -1) { - log_warn("queue-proc: open: %s", path); - return (0); - } - - imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_COMMIT, 0, 0, fd, &msgid, - sizeof(msgid)); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_message_delete(uint32_t msgid) -{ - int r; - - imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_DELETE, 0, 0, -1, &msgid, - sizeof(msgid)); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_message_fd_r(uint32_t msgid) -{ - imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_FD_R, 0, 0, -1, &msgid, - sizeof(msgid)); - - queue_proc_call(); - queue_proc_end(); - - return (imsg.fd); -} - -static int -queue_proc_envelope_create(uint32_t msgid, const char *buf, size_t len, - uint64_t *evpid) -{ - struct ibuf *b; - int r; - - msgid = evpid_to_msgid(*evpid); - b = imsg_create(&ibuf, PROC_QUEUE_ENVELOPE_CREATE, 0, 0, - sizeof(msgid) + len); - if (imsg_add(b, &msgid, sizeof(msgid)) == -1 || - imsg_add(b, buf, len) == -1) - return (0); - imsg_close(&ibuf, b); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - if (r == 1) - queue_proc_read(evpid, sizeof(*evpid)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_envelope_delete(uint64_t evpid) -{ - int r; - - imsg_compose(&ibuf, PROC_QUEUE_ENVELOPE_DELETE, 0, 0, -1, &evpid, - sizeof(evpid)); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_envelope_update(uint64_t evpid, const char *buf, size_t len) -{ - struct ibuf *b; - int r; - - b = imsg_create(&ibuf, PROC_QUEUE_ENVELOPE_UPDATE, 0, 0, - len + sizeof(evpid)); - if (imsg_add(b, &evpid, sizeof(evpid)) == -1 || - imsg_add(b, buf, len) == -1) - return (0); - imsg_close(&ibuf, b); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_envelope_load(uint64_t evpid, char *buf, size_t len) -{ - int r; - - imsg_compose(&ibuf, PROC_QUEUE_ENVELOPE_LOAD, 0, 0, -1, &evpid, - sizeof(evpid)); - - queue_proc_call(); - - if (rlen > len) { - log_warnx("warn: queue-proc: buf too small"); - fatalx("queue-proc: exiting"); - } - - r = rlen; - queue_proc_read(buf, rlen); - queue_proc_end(); - - return (r); -} - -static int -queue_proc_envelope_walk(uint64_t *evpid, char *buf, size_t len) -{ - int r; - - imsg_compose(&ibuf, PROC_QUEUE_ENVELOPE_WALK, 0, 0, -1, NULL, 0); - - queue_proc_call(); - queue_proc_read(&r, sizeof(r)); - - if (r > 0) { - queue_proc_read(evpid, sizeof(*evpid)); - if (rlen > len) { - log_warnx("warn: queue-proc: buf too small"); - fatalx("queue-proc: exiting"); - } - if (r != (int)rlen) { - log_warnx("warn: queue-proc: len mismatch"); - fatalx("queue-proc: exiting"); - } - queue_proc_read(buf, rlen); - } - queue_proc_end(); - - return (r); -} - -static int -queue_proc_init(struct passwd *pw, int server, const char *conf) -{ - uint32_t version; - int fd; - - fd = fork_proc_backend("queue", conf, "queue-proc"); - if (fd == -1) - fatalx("queue-proc: exiting"); - - imsg_init(&ibuf, fd); - - version = PROC_QUEUE_API_VERSION; - imsg_compose(&ibuf, PROC_QUEUE_INIT, 0, 0, -1, - &version, sizeof(version)); - - queue_api_on_close(queue_proc_close); - queue_api_on_message_create(queue_proc_message_create); - queue_api_on_message_commit(queue_proc_message_commit); - queue_api_on_message_delete(queue_proc_message_delete); - queue_api_on_message_fd_r(queue_proc_message_fd_r); - queue_api_on_envelope_create(queue_proc_envelope_create); - queue_api_on_envelope_delete(queue_proc_envelope_delete); - queue_api_on_envelope_update(queue_proc_envelope_update); - queue_api_on_envelope_load(queue_proc_envelope_load); - queue_api_on_envelope_walk(queue_proc_envelope_walk); - - queue_proc_call(); - queue_proc_end(); - - return (1); -} - -struct queue_backend queue_backend_proc = { - queue_proc_init, -}; diff --git a/smtpd/queue_ram.c b/smtpd/queue_ram.c deleted file mode 100644 index 50ce17e1..00000000 --- a/smtpd/queue_ram.c +++ /dev/null @@ -1,336 +0,0 @@ -/* $OpenBSD: queue_ram.c,v 1.9 2018/12/30 23:09:58 guenther Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -struct qr_envelope { - char *buf; - size_t len; -}; - -struct qr_message { - char *buf; - size_t len; - struct tree envelopes; -}; - -static struct tree messages; - -static struct qr_message * -get_message(uint32_t msgid) -{ - struct qr_message *msg; - - msg = tree_get(&messages, msgid); - if (msg == NULL) - log_warn("warn: queue-ram: message not found"); - - return (msg); -} - -static int -queue_ram_message_create(uint32_t *msgid) -{ - struct qr_message *msg; - - msg = calloc(1, sizeof(*msg)); - if (msg == NULL) { - log_warn("warn: queue-ram: calloc"); - return (0); - } - tree_init(&msg->envelopes); - - do { - *msgid = queue_generate_msgid(); - } while (tree_check(&messages, *msgid)); - - tree_xset(&messages, *msgid, msg); - - return (1); -} - -static int -queue_ram_message_commit(uint32_t msgid, const char *path) -{ - struct qr_message *msg; - struct stat sb; - size_t n; - FILE *f; - int ret; - - if ((msg = tree_get(&messages, msgid)) == NULL) { - log_warnx("warn: queue-ram: msgid not found"); - return (0); - } - - f = fopen(path, "rb"); - if (f == NULL) { - log_warn("warn: queue-ram: fopen: %s", path); - return (0); - } - if (fstat(fileno(f), &sb) == -1) { - log_warn("warn: queue-ram: fstat"); - fclose(f); - return (0); - } - - msg->len = sb.st_size; - msg->buf = malloc(msg->len); - if (msg->buf == NULL) { - log_warn("warn: queue-ram: malloc"); - fclose(f); - return (0); - } - - ret = 0; - n = fread(msg->buf, 1, msg->len, f); - if (ferror(f)) - log_warn("warn: queue-ram: fread"); - else if ((off_t)n != sb.st_size) - log_warnx("warn: queue-ram: bad read"); - else { - ret = 1; - stat_increment("queue.ram.message.size", msg->len); - } - fclose(f); - - return (ret); -} - -static int -queue_ram_message_delete(uint32_t msgid) -{ - struct qr_message *msg; - struct qr_envelope *evp; - uint64_t evpid; - - if ((msg = tree_pop(&messages, msgid)) == NULL) { - log_warnx("warn: queue-ram: not found"); - return (0); - } - while (tree_poproot(&messages, &evpid, (void**)&evp)) { - stat_decrement("queue.ram.envelope.size", evp->len); - free(evp->buf); - free(evp); - } - stat_decrement("queue.ram.message.size", msg->len); - free(msg->buf); - free(msg); - return (0); -} - -static int -queue_ram_message_fd_r(uint32_t msgid) -{ - struct qr_message *msg; - size_t n; - FILE *f; - int fd, fd2; - - if ((msg = tree_get(&messages, msgid)) == NULL) { - log_warnx("warn: queue-ram: not found"); - return (-1); - } - - fd = mktmpfile(); - if (fd == -1) { - log_warn("warn: queue-ram: mktmpfile"); - return (-1); - } - - fd2 = dup(fd); - if (fd2 == -1) { - log_warn("warn: queue-ram: dup"); - close(fd); - return (-1); - } - f = fdopen(fd2, "w"); - if (f == NULL) { - log_warn("warn: queue-ram: fdopen"); - close(fd); - close(fd2); - return (-1); - } - n = fwrite(msg->buf, 1, msg->len, f); - if (n != msg->len) { - log_warn("warn: queue-ram: write"); - close(fd); - fclose(f); - return (-1); - } - fclose(f); - lseek(fd, 0, SEEK_SET); - return (fd); -} - -static int -queue_ram_envelope_create(uint32_t msgid, const char *buf, size_t len, - uint64_t *evpid) -{ - struct qr_envelope *evp; - struct qr_message *msg; - - if ((msg = get_message(msgid)) == NULL) - return (0); - - do { - *evpid = queue_generate_evpid(msgid); - } while (tree_check(&msg->envelopes, *evpid)); - evp = calloc(1, sizeof *evp); - if (evp == NULL) { - log_warn("warn: queue-ram: calloc"); - return (0); - } - evp->len = len; - evp->buf = malloc(len); - if (evp->buf == NULL) { - log_warn("warn: queue-ram: malloc"); - free(evp); - return (0); - } - memmove(evp->buf, buf, len); - tree_xset(&msg->envelopes, *evpid, evp); - stat_increment("queue.ram.envelope.size", len); - return (1); -} - -static int -queue_ram_envelope_delete(uint64_t evpid) -{ - struct qr_envelope *evp; - struct qr_message *msg; - - if ((msg = get_message(evpid_to_msgid(evpid))) == NULL) - return (0); - - if ((evp = tree_pop(&msg->envelopes, evpid)) == NULL) { - log_warnx("warn: queue-ram: not found"); - return (0); - } - stat_decrement("queue.ram.envelope.size", evp->len); - free(evp->buf); - free(evp); - if (tree_empty(&msg->envelopes)) { - tree_xpop(&messages, evpid_to_msgid(evpid)); - stat_decrement("queue.ram.message.size", msg->len); - free(msg->buf); - free(msg); - } - return (1); -} - -static int -queue_ram_envelope_update(uint64_t evpid, const char *buf, size_t len) -{ - struct qr_envelope *evp; - struct qr_message *msg; - void *tmp; - - if ((msg = get_message(evpid_to_msgid(evpid))) == NULL) - return (0); - - if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) { - log_warn("warn: queue-ram: not found"); - return (0); - } - tmp = malloc(len); - if (tmp == NULL) { - log_warn("warn: queue-ram: malloc"); - return (0); - } - memmove(tmp, buf, len); - free(evp->buf); - evp->len = len; - evp->buf = tmp; - stat_decrement("queue.ram.envelope.size", evp->len); - stat_increment("queue.ram.envelope.size", len); - return (1); -} - -static int -queue_ram_envelope_load(uint64_t evpid, char *buf, size_t len) -{ - struct qr_envelope *evp; - struct qr_message *msg; - - if ((msg = get_message(evpid_to_msgid(evpid))) == NULL) - return (0); - - if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) { - log_warn("warn: queue-ram: not found"); - return (0); - } - if (len < evp->len) { - log_warnx("warn: queue-ram: buffer too small"); - return (0); - } - memmove(buf, evp->buf, evp->len); - return (evp->len); -} - -static int -queue_ram_envelope_walk(uint64_t *evpid, char *buf, size_t len) -{ - return (-1); -} - -static int -queue_ram_init(struct passwd *pw, int server, const char * conf) -{ - tree_init(&messages); - - queue_api_on_message_create(queue_ram_message_create); - queue_api_on_message_commit(queue_ram_message_commit); - queue_api_on_message_delete(queue_ram_message_delete); - queue_api_on_message_fd_r(queue_ram_message_fd_r); - queue_api_on_envelope_create(queue_ram_envelope_create); - queue_api_on_envelope_delete(queue_ram_envelope_delete); - queue_api_on_envelope_update(queue_ram_envelope_update); - queue_api_on_envelope_load(queue_ram_envelope_load); - queue_api_on_envelope_walk(queue_ram_envelope_walk); - - return (1); -} - -struct queue_backend queue_backend_ram = { - queue_ram_init, -}; diff --git a/smtpd/report_smtp.c b/smtpd/report_smtp.c deleted file mode 100644 index 7802eaae..00000000 --- a/smtpd/report_smtp.c +++ /dev/null @@ -1,335 +0,0 @@ -/* $OpenBSD: report_smtp.c,v 1.11 2020/01/07 23:03:37 gilles Exp $ */ - -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/uio.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <limits.h> -#include <inttypes.h> -#include <openssl/ssl.h> -#include <resolv.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) -#include <vis.h> -#else -#include "bsd-vis.h" -#endif - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" -#include "rfc5322.h" - -void -report_smtp_link_connect(const char *direction, uint64_t qid, const char *rdns, int fcrdns, - const struct sockaddr_storage *ss_src, - const struct sockaddr_storage *ss_dest) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_LINK_CONNECT, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, rdns); - m_add_int(p_lka, fcrdns); - m_add_sockaddr(p_lka, (const struct sockaddr *)ss_src); - m_add_sockaddr(p_lka, (const struct sockaddr *)ss_dest); - m_close(p_lka); -} - -void -report_smtp_link_greeting(const char *direction, uint64_t qid, - const char *domain) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_LINK_GREETING, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, domain); - m_close(p_lka); -} - -void -report_smtp_link_identify(const char *direction, uint64_t qid, const char *method, const char *identity) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_LINK_IDENTIFY, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, method); - m_add_string(p_lka, identity); - m_close(p_lka); -} - -void -report_smtp_link_tls(const char *direction, uint64_t qid, const char *ssl) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_LINK_TLS, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, ssl); - m_close(p_lka); -} - -void -report_smtp_link_disconnect(const char *direction, uint64_t qid) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_LINK_DISCONNECT, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_close(p_lka); -} - -void -report_smtp_link_auth(const char *direction, uint64_t qid, const char *user, const char *result) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_LINK_AUTH, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, user); - m_add_string(p_lka, result); - m_close(p_lka); -} - -void -report_smtp_tx_reset(const char *direction, uint64_t qid, uint32_t msgid) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_RESET, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_close(p_lka); -} - -void -report_smtp_tx_begin(const char *direction, uint64_t qid, uint32_t msgid) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_BEGIN, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_close(p_lka); -} - -void -report_smtp_tx_mail(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_MAIL, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_add_string(p_lka, address); - m_add_int(p_lka, ok); - m_close(p_lka); -} - -void -report_smtp_tx_rcpt(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_RCPT, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_add_string(p_lka, address); - m_add_int(p_lka, ok); - m_close(p_lka); -} - -void -report_smtp_tx_envelope(const char *direction, uint64_t qid, uint32_t msgid, uint64_t evpid) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_ENVELOPE, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_add_id(p_lka, evpid); - m_close(p_lka); -} - -void -report_smtp_tx_data(const char *direction, uint64_t qid, uint32_t msgid, int ok) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_DATA, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_add_int(p_lka, ok); - m_close(p_lka); -} - -void -report_smtp_tx_commit(const char *direction, uint64_t qid, uint32_t msgid, size_t msgsz) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_COMMIT, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_add_size(p_lka, msgsz); - m_close(p_lka); -} - -void -report_smtp_tx_rollback(const char *direction, uint64_t qid, uint32_t msgid) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TX_ROLLBACK, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_u32(p_lka, msgid); - m_close(p_lka); -} - -void -report_smtp_protocol_client(const char *direction, uint64_t qid, const char *command) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_PROTOCOL_CLIENT, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, command); - m_close(p_lka); -} - -void -report_smtp_protocol_server(const char *direction, uint64_t qid, const char *response) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_PROTOCOL_SERVER, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_string(p_lka, response); - m_close(p_lka); -} - -void -report_smtp_filter_response(const char *direction, uint64_t qid, int phase, int response, const char *param) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_FILTER_RESPONSE, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_add_int(p_lka, phase); - m_add_int(p_lka, response); - m_add_string(p_lka, param); - m_close(p_lka); -} - -void -report_smtp_timeout(const char *direction, uint64_t qid) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - m_create(p_lka, IMSG_REPORT_SMTP_TIMEOUT, 0, 0, -1); - m_add_string(p_lka, direction); - m_add_timeval(p_lka, &tv); - m_add_id(p_lka, qid); - m_close(p_lka); -} diff --git a/smtpd/resolver.c b/smtpd/resolver.c deleted file mode 100644 index f0f0f8ea..00000000 --- a/smtpd/resolver.c +++ /dev/null @@ -1,462 +0,0 @@ -/* $OpenBSD: resolver.c,v 1.5 2019/06/13 11:45:35 eric Exp $ */ - -/* - * Copyright (c) 2017-2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/queue.h> -#include <netinet/in.h> - -#include <asr.h> -#include <ctype.h> -#include <errno.h> -#include <imsg.h> -#include <limits.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -#define p_resolver p_lka - -struct request { - SPLAY_ENTRY(request) entry; - uint32_t id; - void (*cb_ai)(void *, int, struct addrinfo *); - void (*cb_ni)(void *, int, const char *, const char *); - void (*cb_res)(void *, int, int, int, const void *, int); - void *arg; - struct addrinfo *ai; -}; - -struct session { - uint32_t reqid; - struct mproc *proc; - char *host; - char *serv; -}; - -SPLAY_HEAD(reqtree, request); - -static void resolver_init(void); -static void resolver_getaddrinfo_cb(struct asr_result *, void *); -static void resolver_getnameinfo_cb(struct asr_result *, void *); -static void resolver_res_query_cb(struct asr_result *, void *); - -static int request_cmp(struct request *, struct request *); -SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp); - -/* musl work-around */ -void portable_freeaddrinfo(struct addrinfo *); - -static struct reqtree reqs; - -void -resolver_getaddrinfo(const char *hostname, const char *servname, - const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *), - void *arg) -{ - struct request *req; - - resolver_init(); - - req = calloc(1, sizeof(*req)); - if (req == NULL) { - cb(arg, EAI_MEMORY, NULL); - return; - } - - while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req)) - req->id = arc4random(); - req->cb_ai = cb; - req->arg = arg; - - SPLAY_INSERT(reqtree, &reqs, req); - - m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1); - m_add_int(p_resolver, hints ? hints->ai_flags : 0); - m_add_int(p_resolver, hints ? hints->ai_family : 0); - m_add_int(p_resolver, hints ? hints->ai_socktype : 0); - m_add_int(p_resolver, hints ? hints->ai_protocol : 0); - m_add_string(p_resolver, hostname); - m_add_string(p_resolver, servname); - m_close(p_resolver); -} - -void -resolver_getnameinfo(const struct sockaddr *sa, int flags, - void(*cb)(void *, int, const char *, const char *), void *arg) -{ - struct request *req; - - resolver_init(); - - req = calloc(1, sizeof(*req)); - if (req == NULL) { - cb(arg, EAI_MEMORY, NULL, NULL); - return; - } - - while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req)) - req->id = arc4random(); - req->cb_ni = cb; - req->arg = arg; - - SPLAY_INSERT(reqtree, &reqs, req); - - m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1); - m_add_sockaddr(p_resolver, sa); - m_add_int(p_resolver, flags); - m_close(p_resolver); -} - -void -resolver_res_query(const char *dname, int class, int type, - void (*cb)(void *, int, int, int, const void *, int), void *arg) -{ - struct request *req; - - resolver_init(); - - req = calloc(1, sizeof(*req)); - if (req == NULL) { - cb(arg, NETDB_INTERNAL, 0, 0, NULL, 0); - return; - } - - while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req)) - req->id = arc4random(); - req->cb_res = cb; - req->arg = arg; - - SPLAY_INSERT(reqtree, &reqs, req); - - m_create(p_resolver, IMSG_RES_QUERY, req->id, 0, -1); - m_add_string(p_resolver, dname); - m_add_int(p_resolver, class); - m_add_int(p_resolver, type); - m_close(p_resolver); -} - -void -resolver_dispatch_request(struct mproc *proc, struct imsg *imsg) -{ - const char *hostname, *servname, *dname; - struct session *s; - struct asr_query *q; - struct addrinfo hints; - struct sockaddr_storage ss; - struct sockaddr *sa; - struct msg m; - uint32_t reqid; - int class, type, flags, save_errno; - - reqid = imsg->hdr.peerid; - m_msg(&m, imsg); - - switch (imsg->hdr.type) { - - case IMSG_GETADDRINFO: - servname = NULL; - memset(&hints, 0 , sizeof(hints)); - m_get_int(&m, &hints.ai_flags); - m_get_int(&m, &hints.ai_family); - m_get_int(&m, &hints.ai_socktype); - m_get_int(&m, &hints.ai_protocol); - m_get_string(&m, &hostname); - m_get_string(&m, &servname); - m_end(&m); - - s = NULL; - q = NULL; - if ((s = calloc(1, sizeof(*s))) && - (q = getaddrinfo_async(hostname, servname, &hints, NULL)) && - (event_asr_run(q, resolver_getaddrinfo_cb, s))) { - s->reqid = reqid; - s->proc = proc; - break; - } - save_errno = errno; - - if (q) - asr_abort(q); - if (s) - free(s); - - m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1); - m_add_int(proc, EAI_SYSTEM); - m_add_int(proc, save_errno); - m_close(proc); - break; - - case IMSG_GETNAMEINFO: - sa = (struct sockaddr*)&ss; - m_get_sockaddr(&m, sa); - m_get_int(&m, &flags); - m_end(&m); - - s = NULL; - q = NULL; - if ((s = calloc(1, sizeof(*s))) && - (s->host = malloc(NI_MAXHOST)) && - (s->serv = malloc(NI_MAXSERV)) && - (q = getnameinfo_async(sa, SA_LEN(sa), s->host, NI_MAXHOST, - s->serv, NI_MAXSERV, flags, NULL)) && - (event_asr_run(q, resolver_getnameinfo_cb, s))) { - s->reqid = reqid; - s->proc = proc; - break; - } - save_errno = errno; - - if (q) - asr_abort(q); - if (s) { - free(s->host); - free(s->serv); - free(s); - } - - m_create(proc, IMSG_GETNAMEINFO, reqid, 0, -1); - m_add_int(proc, EAI_SYSTEM); - m_add_int(proc, save_errno); - m_add_string(proc, NULL); - m_add_string(proc, NULL); - m_close(proc); - break; - - case IMSG_RES_QUERY: - m_get_string(&m, &dname); - m_get_int(&m, &class); - m_get_int(&m, &type); - m_end(&m); - - s = NULL; - q = NULL; - if ((s = calloc(1, sizeof(*s))) && - (q = res_query_async(dname, class, type, NULL)) && - (event_asr_run(q, resolver_res_query_cb, s))) { - s->reqid = reqid; - s->proc = proc; - break; - } - save_errno = errno; - - if (q) - asr_abort(q); - if (s) - free(s); - - m_create(proc, IMSG_RES_QUERY, reqid, 0, -1); - m_add_int(proc, NETDB_INTERNAL); - m_add_int(proc, save_errno); - m_add_int(proc, 0); - m_add_int(proc, 0); - m_add_data(proc, NULL, 0); - m_close(proc); - break; - - default: - fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type)); - } -} - -void -resolver_dispatch_result(struct mproc *proc, struct imsg *imsg) -{ - struct request key, *req; - struct sockaddr_storage ss; - struct addrinfo *ai; - struct msg m; - const char *cname, *host, *serv; - const void *data; - size_t datalen; - int gai_errno, herrno, rcode, count; - - key.id = imsg->hdr.peerid; - req = SPLAY_FIND(reqtree, &reqs, &key); - if (req == NULL) - fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid); - - m_msg(&m, imsg); - - switch (imsg->hdr.type) { - - case IMSG_GETADDRINFO: - ai = calloc(1, sizeof(*ai)); - if (ai == NULL) { - log_warn("%s: calloc", __func__); - break; - } - m_get_int(&m, &ai->ai_flags); - m_get_int(&m, &ai->ai_family); - m_get_int(&m, &ai->ai_socktype); - m_get_int(&m, &ai->ai_protocol); - m_get_sockaddr(&m, (struct sockaddr *)&ss); - m_get_string(&m, &cname); - m_end(&m); - - ai->ai_addr = malloc(SS_LEN(&ss)); - if (ai->ai_addr == NULL) { - log_warn("%s: malloc", __func__); - free(ai); - break; - } - - memmove(ai->ai_addr, &ss, SS_LEN(&ss)); - - if (cname) { - ai->ai_canonname = strdup(cname); - if (ai->ai_canonname == NULL) { - log_warn("%s: strdup", __func__); - free(ai->ai_addr); - free(ai); - break; - } - } - - ai->ai_next = req->ai; - req->ai = ai; - break; - - case IMSG_GETADDRINFO_END: - m_get_int(&m, &gai_errno); - m_get_int(&m, &errno); - m_end(&m); - - SPLAY_REMOVE(reqtree, &reqs, req); - req->cb_ai(req->arg, gai_errno, req->ai); - free(req); - break; - - case IMSG_GETNAMEINFO: - m_get_int(&m, &gai_errno); - m_get_int(&m, &errno); - m_get_string(&m, &host); - m_get_string(&m, &serv); - m_end(&m); - - SPLAY_REMOVE(reqtree, &reqs, req); - req->cb_ni(req->arg, gai_errno, host, serv); - free(req); - break; - - case IMSG_RES_QUERY: - m_get_int(&m, &herrno); - m_get_int(&m, &errno); - m_get_int(&m, &rcode); - m_get_int(&m, &count); - m_get_data(&m, &data, &datalen); - m_end(&m); - - SPLAY_REMOVE(reqtree, &reqs, req); - req->cb_res(req->arg, herrno, rcode, count, data, datalen); - free(req); - break; - } -} - -static void -resolver_init(void) -{ - static int init = 0; - - if (init == 0) { - SPLAY_INIT(&reqs); - init = 1; - } -} - -static void -resolver_getaddrinfo_cb(struct asr_result *ar, void *arg) -{ - struct session *s = arg; - struct addrinfo *ai; - - for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) { - m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1); - m_add_int(s->proc, ai->ai_flags); - m_add_int(s->proc, ai->ai_family); - m_add_int(s->proc, ai->ai_socktype); - m_add_int(s->proc, ai->ai_protocol); - m_add_sockaddr(s->proc, ai->ai_addr); - m_add_string(s->proc, ai->ai_canonname); - m_close(s->proc); - } - - m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1); - m_add_int(s->proc, ar->ar_gai_errno); - m_add_int(s->proc, ar->ar_errno); - m_close(s->proc); - - if (ar->ar_addrinfo) - portable_freeaddrinfo(ar->ar_addrinfo); - free(s); -} - -static void -resolver_getnameinfo_cb(struct asr_result *ar, void *arg) -{ - struct session *s = arg; - - m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1); - m_add_int(s->proc, ar->ar_gai_errno); - m_add_int(s->proc, ar->ar_errno); - m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->host); - m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->serv); - m_close(s->proc); - - free(s->host); - free(s->serv); - free(s); -} - -static void -resolver_res_query_cb(struct asr_result *ar, void *arg) -{ - struct session *s = arg; - - m_create(s->proc, IMSG_RES_QUERY, s->reqid, 0, -1); - m_add_int(s->proc, ar->ar_h_errno); - m_add_int(s->proc, ar->ar_errno); - m_add_int(s->proc, ar->ar_rcode); - m_add_int(s->proc, ar->ar_count); - m_add_data(s->proc, ar->ar_data, ar->ar_datalen); - m_close(s->proc); - - free(ar->ar_data); - free(s); -} - -static int -request_cmp(struct request *a, struct request *b) -{ - if (a->id < b->id) - return -1; - if (a->id > b->id) - return 1; - return 0; -} - -SPLAY_GENERATE(reqtree, request, entry, request_cmp); diff --git a/smtpd/rfc5322.c b/smtpd/rfc5322.c deleted file mode 100644 index 0af66772..00000000 --- a/smtpd/rfc5322.c +++ /dev/null @@ -1,266 +0,0 @@ -/* $OpenBSD: rfc5322.c,v 1.2 2018/10/24 18:59:29 gilles Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> - -#include "rfc5322.h" - -struct buf { - char *buf; - size_t bufsz; - size_t buflen; - size_t bufmax; -}; - -static int buf_alloc(struct buf *, size_t); -static int buf_grow(struct buf *, size_t); -static int buf_cat(struct buf *, const char *); - -struct rfc5322_parser { - const char *line; - int state; /* last parser state */ - int next; /* parser needs data */ - int unfold; - const char *currhdr; - struct buf hdr; - struct buf val; -}; - -struct rfc5322_parser * -rfc5322_parser_new(void) -{ - struct rfc5322_parser *parser; - - parser = calloc(1, sizeof(*parser)); - if (parser == NULL) - return NULL; - - rfc5322_clear(parser); - parser->hdr.bufmax = 1024; - parser->val.bufmax = 65536; - - return parser; -} - -void -rfc5322_free(struct rfc5322_parser *parser) -{ - free(parser->hdr.buf); - free(parser->val.buf); - free(parser); -} - -void -rfc5322_clear(struct rfc5322_parser *parser) -{ - parser->line = NULL; - parser->state = RFC5322_NONE; - parser->next = 0; - parser->hdr.buflen = 0; - parser->val.buflen = 0; -} - -int -rfc5322_push(struct rfc5322_parser *parser, const char *line) -{ - if (parser->line) { - errno = EALREADY; - return -1; - } - - parser->line = line; - parser->next = 0; - - return 0; -} - -int -rfc5322_unfold_header(struct rfc5322_parser *parser) -{ - if (parser->unfold) { - errno = EALREADY; - return -1; - } - - if (parser->currhdr == NULL) { - errno = EOPNOTSUPP; - return -1; - } - - if (buf_cat(&parser->val, parser->currhdr) == -1) - return -1; - - parser->currhdr = NULL; - parser->unfold = 1; - - return 0; -} - -static int -_rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res) -{ - size_t len; - const char *pos, *line; - - line = parser->line; - - switch(parser->state) { - - case RFC5322_HEADER_START: - case RFC5322_HEADER_CONT: - res->hdr = parser->hdr.buf; - - if (line && (line[0] == ' ' || line[0] == '\t')) { - parser->line = NULL; - parser->next = 1; - if (parser->unfold) { - if (buf_cat(&parser->val, "\n") == -1 || - buf_cat(&parser->val, line) == -1) - return -1; - } - res->value = line; - return RFC5322_HEADER_CONT; - } - - if (parser->unfold) { - parser->val.buflen = 0; - parser->unfold = 0; - res->value = parser->val.buf; - } - return RFC5322_HEADER_END; - - case RFC5322_NONE: - case RFC5322_HEADER_END: - if (line && (pos = strchr(line, ':'))) { - len = pos - line; - if (buf_grow(&parser->hdr, len + 1) == -1) - return -1; - (void)memcpy(parser->hdr.buf, line, len); - parser->hdr.buf[len] = '\0'; - parser->hdr.buflen = len + 1; - parser->line = NULL; - parser->next = 1; - parser->currhdr = pos + 1; - res->hdr = parser->hdr.buf; - res->value = pos + 1; - return RFC5322_HEADER_START; - } - - return RFC5322_END_OF_HEADERS; - - case RFC5322_END_OF_HEADERS: - if (line == NULL) - return RFC5322_END_OF_MESSAGE; - - if (line[0] == '\0') { - parser->line = NULL; - parser->next = 1; - res->value = line; - return RFC5322_BODY_START; - } - - errno = EINVAL; - return -1; - - case RFC5322_BODY_START: - case RFC5322_BODY: - if (line == NULL) - return RFC5322_END_OF_MESSAGE; - - parser->line = NULL; - parser->next = 1; - res->value = line; - return RFC5322_BODY; - - case RFC5322_END_OF_MESSAGE: - errno = ENOMSG; - return -1; - - default: - errno = EINVAL; - return -1; - } -} - -int -rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res) -{ - memset(res, 0, sizeof(*res)); - - if (parser->next) - return RFC5322_NONE; - - return (parser->state = _rfc5322_next(parser, res)); -} - -static int -buf_alloc(struct buf *b, size_t need) -{ - char *buf; - size_t alloc; - - if (b->buf && b->bufsz >= need) - return 0; - - if (need >= b->bufmax) { - errno = ERANGE; - return -1; - } - -#define N 256 - alloc = N * (need / N) + ((need % N) ? N : 0); -#undef N - buf = reallocarray(b->buf, alloc, 1); - if (buf == NULL) - return -1; - - b->buf = buf; - b->bufsz = alloc; - - return 0; -} - -static int -buf_grow(struct buf *b, size_t sz) -{ - if (SIZE_T_MAX - b->buflen <= sz) { - errno = ERANGE; - return -1; - } - - return buf_alloc(b, b->buflen + sz); -} - -static int -buf_cat(struct buf *b, const char *s) -{ - size_t len = strlen(s); - - if (buf_grow(b, len + 1) == -1) - return -1; - - (void)memmove(b->buf + b->buflen, s, len + 1); - b->buflen += len; - return 0; -} diff --git a/smtpd/rfc5322.h b/smtpd/rfc5322.h deleted file mode 100644 index 0979bd4c..00000000 --- a/smtpd/rfc5322.h +++ /dev/null @@ -1,41 +0,0 @@ -/* $OpenBSD: rfc5322.h,v 1.1 2018/08/23 10:07:06 eric Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -struct rfc5322_result { - const char *hdr; - const char *value; -}; - -#define RFC5322_ERR -1 -#define RFC5322_NONE 0 -#define RFC5322_HEADER_START 1 -#define RFC5322_HEADER_CONT 2 -#define RFC5322_HEADER_END 3 -#define RFC5322_END_OF_HEADERS 4 -#define RFC5322_BODY_START 5 -#define RFC5322_BODY 6 -#define RFC5322_END_OF_MESSAGE 7 - -struct rfc5322_parser; - -struct rfc5322_parser *rfc5322_parser_new(void); -void rfc5322_free(struct rfc5322_parser *); -void rfc5322_clear(struct rfc5322_parser *); -int rfc5322_push(struct rfc5322_parser *, const char *); -int rfc5322_next(struct rfc5322_parser *, struct rfc5322_result *); -int rfc5322_unfold_header(struct rfc5322_parser *); diff --git a/smtpd/ruleset.c b/smtpd/ruleset.c deleted file mode 100644 index 719a2913..00000000 --- a/smtpd/ruleset.c +++ /dev/null @@ -1,265 +0,0 @@ -/* $OpenBSD: ruleset.c,v 1.47 2019/11/25 14:18:33 gilles Exp $ */ - -/* - * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <string.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define MATCH_RESULT(r, neg) ((r) == -1 ? -1 : ((neg) < 0 ? !(r) : (r))) - -static int -ruleset_match_tag(struct rule *r, const struct envelope *evp) -{ - int ret; - struct table *table; - enum table_service service = K_STRING; - - if (!r->flag_tag) - return 1; - - if (r->flag_tag_regex) - service = K_REGEX; - - table = table_find(env, r->table_tag); - ret = table_match(table, service, evp->tag); - - return MATCH_RESULT(ret, r->flag_tag); -} - -static int -ruleset_match_from(struct rule *r, const struct envelope *evp) -{ - int ret; - int has_rdns; - const char *key; - struct table *table; - enum table_service service = K_NETADDR; - - if (!r->flag_from) - return 1; - - if (evp->flags & EF_INTERNAL) { - /* if expanded from an empty table_from, skip rule - * if no table - */ - if (r->table_from == NULL) - return 0; - key = "local"; - } - else if (r->flag_from_rdns) { - has_rdns = strcmp(evp->hostname, "<unknown>") != 0; - if (r->table_from == NULL) - return MATCH_RESULT(has_rdns, r->flag_from); - if (!has_rdns) - return 0; - key = evp->hostname; - } - else { - key = ss_to_text(&evp->ss); - if (r->flag_from_socket) { - if (strcmp(key, "local") == 0) - return MATCH_RESULT(1, r->flag_from); - else - return r->flag_from < 0 ? 1 : 0; - } - } - if (r->flag_from_regex) - service = K_REGEX; - - table = table_find(env, r->table_from); - ret = table_match(table, service, key); - - return MATCH_RESULT(ret, r->flag_from); -} - -static int -ruleset_match_to(struct rule *r, const struct envelope *evp) -{ - int ret; - struct table *table; - enum table_service service = K_DOMAIN; - - if (!r->flag_for) - return 1; - - if (r->flag_for_regex) - service = K_REGEX; - - table = table_find(env, r->table_for); - ret = table_match(table, service, evp->dest.domain); - - return MATCH_RESULT(ret, r->flag_for); -} - -static int -ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp) -{ - int ret; - struct table *table; - enum table_service service = K_DOMAIN; - - if (!r->flag_smtp_helo) - return 1; - - if (r->flag_smtp_helo_regex) - service = K_REGEX; - - table = table_find(env, r->table_smtp_helo); - ret = table_match(table, service, evp->helo); - - return MATCH_RESULT(ret, r->flag_smtp_helo); -} - -static int -ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp) -{ - if (!r->flag_smtp_starttls) - return 1; - - /* XXX - not until TLS flag is added to envelope */ - return -1; -} - -static int -ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp) -{ - int ret; - struct table *table; - enum table_service service; - - if (!r->flag_smtp_auth) - return 1; - - if (!(evp->flags & EF_AUTHENTICATED)) - ret = 0; - else if (r->table_smtp_auth) { - - if (r->flag_smtp_auth_regex) - service = K_REGEX; - else - service = strchr(evp->username, '@') ? - K_MAILADDR : K_STRING; - table = table_find(env, r->table_smtp_auth); - ret = table_match(table, service, evp->username); - } - else - ret = 1; - - return MATCH_RESULT(ret, r->flag_smtp_auth); -} - -static int -ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp) -{ - int ret; - const char *key; - struct table *table; - enum table_service service = K_MAILADDR; - - if (!r->flag_smtp_mail_from) - return 1; - - if (r->flag_smtp_mail_from_regex) - service = K_REGEX; - - if ((key = mailaddr_to_text(&evp->sender)) == NULL) - return -1; - - table = table_find(env, r->table_smtp_mail_from); - ret = table_match(table, service, key); - - return MATCH_RESULT(ret, r->flag_smtp_mail_from); -} - -static int -ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp) -{ - int ret; - const char *key; - struct table *table; - enum table_service service = K_MAILADDR; - - if (!r->flag_smtp_rcpt_to) - return 1; - - if (r->flag_smtp_rcpt_to_regex) - service = K_REGEX; - - if ((key = mailaddr_to_text(&evp->dest)) == NULL) - return -1; - - table = table_find(env, r->table_smtp_rcpt_to); - ret = table_match(table, service, key); - - return MATCH_RESULT(ret, r->flag_smtp_rcpt_to); -} - -struct rule * -ruleset_match(const struct envelope *evp) -{ - struct rule *r; - int i = 0; - -#define MATCH_EVAL(x) \ - switch ((x)) { \ - case -1: goto tempfail; \ - case 0: continue; \ - default: break; \ - } - TAILQ_FOREACH(r, env->sc_rules, r_entry) { - ++i; - MATCH_EVAL(ruleset_match_tag(r, evp)); - MATCH_EVAL(ruleset_match_from(r, evp)); - MATCH_EVAL(ruleset_match_to(r, evp)); - MATCH_EVAL(ruleset_match_smtp_helo(r, evp)); - MATCH_EVAL(ruleset_match_smtp_auth(r, evp)); - MATCH_EVAL(ruleset_match_smtp_starttls(r, evp)); - MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp)); - MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp)); - goto matched; - } -#undef MATCH_EVAL - - errno = 0; - log_trace(TRACE_RULES, "no rule matched"); - return (NULL); - -tempfail: - errno = EAGAIN; - log_trace(TRACE_RULES, "temporary failure in processing of a rule"); - return (NULL); - -matched: - log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r)); - return r; -} diff --git a/smtpd/runq.c b/smtpd/runq.c deleted file mode 100644 index 786d36fb..00000000 --- a/smtpd/runq.c +++ /dev/null @@ -1,183 +0,0 @@ -/* $OpenBSD: runq.c,v 1.3 2019/06/14 19:55:25 eric Exp $ */ - -/* - * Copyright (c) 2013,2019 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/uio.h> - -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <time.h> - -#include "smtpd.h" - -struct job { - TAILQ_ENTRY(job) entry; - time_t when; - void *arg; -}; - -struct runq { - TAILQ_HEAD(, job) jobs; - void (*cb)(struct runq *, void *); - struct event ev; -}; - -static void runq_timeout(int, short, void *); - -static struct runq *active; - -static void -runq_reset(struct runq *runq) -{ - struct timeval tv; - struct job *job; - time_t now; - - job = TAILQ_FIRST(&runq->jobs); - if (job == NULL) - return; - - now = time(NULL); - if (job->when <= now) - tv.tv_sec = 0; - else - tv.tv_sec = job->when - now; - tv.tv_usec = 0; - evtimer_add(&runq->ev, &tv); -} - -static void -runq_timeout(int fd, short ev, void *arg) -{ - struct runq *runq = arg; - struct job *job; - time_t now; - - active = runq; - now = time(NULL); - - while((job = TAILQ_FIRST(&runq->jobs))) { - if (job->when > now) - break; - TAILQ_REMOVE(&runq->jobs, job, entry); - runq->cb(runq, job->arg); - free(job); - } - - active = NULL; - runq_reset(runq); -} - -int -runq_init(struct runq **runqp, void (*cb)(struct runq *, void *)) -{ - struct runq *runq; - - runq = malloc(sizeof(*runq)); - if (runq == NULL) - return (0); - - runq->cb = cb; - TAILQ_INIT(&runq->jobs); - evtimer_set(&runq->ev, runq_timeout, runq); - - *runqp = runq; - - return (1); -} - -int -runq_schedule(struct runq *runq, time_t delay, void *arg) -{ - time_t t; - - time(&t); - return runq_schedule_at(runq, t + delay, arg); -} - -int -runq_schedule_at(struct runq *runq, time_t when, void *arg) -{ - struct job *job, *tmpjob; - - job = malloc(sizeof(*job)); - if (job == NULL) - return (0); - - job->arg = arg; - job->when = when; - - TAILQ_FOREACH(tmpjob, &runq->jobs, entry) { - if (tmpjob->when > job->when) { - TAILQ_INSERT_BEFORE(tmpjob, job, entry); - goto done; - } - } - TAILQ_INSERT_TAIL(&runq->jobs, job, entry); - - done: - if (runq != active && job == TAILQ_FIRST(&runq->jobs)) { - evtimer_del(&runq->ev); - runq_reset(runq); - } - return (1); -} - -int -runq_cancel(struct runq *runq, void *arg) -{ - struct job *job, *first; - - first = TAILQ_FIRST(&runq->jobs); - TAILQ_FOREACH(job, &runq->jobs, entry) { - if (job->arg == arg) { - TAILQ_REMOVE(&runq->jobs, job, entry); - free(job); - if (runq != active && job == first) { - evtimer_del(&runq->ev); - runq_reset(runq); - } - return (1); - } - } - - return (0); -} - -int -runq_pending(struct runq *runq, void *arg, time_t *when) -{ - struct job *job; - - TAILQ_FOREACH(job, &runq->jobs, entry) { - if (job->arg == arg) { - if (when) - *when = job->when; - return (1); - } - } - - return (0); -} diff --git a/smtpd/scheduler.c b/smtpd/scheduler.c deleted file mode 100644 index ea70a83d..00000000 --- a/smtpd/scheduler.c +++ /dev/null @@ -1,618 +0,0 @@ -/* $OpenBSD: scheduler.c,v 1.60 2018/12/30 23:09:58 guenther Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <grp.h> /* needed for setgroups */ -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -static void scheduler_imsg(struct mproc *, struct imsg *); -static void scheduler_shutdown(void); -static void scheduler_reset_events(void); -static void scheduler_timeout(int, short, void *); - -static struct scheduler_backend *backend = NULL; -static struct event ev; -static size_t ninflight = 0; -static int *types; -static uint64_t *evpids; -static uint32_t *msgids; -static struct evpstate *state; - -extern const char *backend_scheduler; - -void -scheduler_imsg(struct mproc *p, struct imsg *imsg) -{ - struct bounce_req_msg req; - struct envelope evp; - struct scheduler_info si; - struct msg m; - uint64_t evpid, id, holdq; - uint32_t msgid; - uint32_t inflight; - size_t n, i; - time_t timestamp; - int v, r, type; - - if (imsg == NULL) - scheduler_shutdown(); - - switch (imsg->hdr.type) { - - case IMSG_QUEUE_ENVELOPE_SUBMIT: - m_msg(&m, imsg); - m_get_envelope(&m, &evp); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: inserting evp:%016" PRIx64, evp.id); - scheduler_info(&si, &evp); - stat_increment("scheduler.envelope.incoming", 1); - backend->insert(&si); - return; - - case IMSG_QUEUE_MESSAGE_COMMIT: - m_msg(&m, imsg); - m_get_msgid(&m, &msgid); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: committing msg:%08" PRIx32, msgid); - n = backend->commit(msgid); - stat_decrement("scheduler.envelope.incoming", n); - stat_increment("scheduler.envelope", n); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_DISCOVER_EVPID: - m_msg(&m, imsg); - m_get_envelope(&m, &evp); - m_end(&m); - r = backend->query(evp.id); - if (r) { - log_debug("debug: scheduler: evp:%016" PRIx64 - " already scheduled", evp.id); - return; - } - log_trace(TRACE_SCHEDULER, - "scheduler: discovering evp:%016" PRIx64, evp.id); - scheduler_info(&si, &evp); - stat_increment("scheduler.envelope.incoming", 1); - backend->insert(&si); - return; - - case IMSG_QUEUE_DISCOVER_MSGID: - m_msg(&m, imsg); - m_get_msgid(&m, &msgid); - m_end(&m); - r = backend->query(msgid); - if (r) { - log_debug("debug: scheduler: msgid:%08" PRIx32 - " already scheduled", msgid); - return; - } - log_trace(TRACE_SCHEDULER, - "scheduler: committing msg:%08" PRIx32, msgid); - n = backend->commit(msgid); - stat_decrement("scheduler.envelope.incoming", n); - stat_increment("scheduler.envelope", n); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_MESSAGE_ROLLBACK: - m_msg(&m, imsg); - m_get_msgid(&m, &msgid); - m_end(&m); - log_trace(TRACE_SCHEDULER, "scheduler: aborting msg:%08" PRIx32, - msgid); - n = backend->rollback(msgid); - stat_decrement("scheduler.envelope.incoming", n); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_ENVELOPE_REMOVE: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_get_u32(&m, &inflight); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: queue requested removal of evp:%016" PRIx64, - evpid); - stat_decrement("scheduler.envelope", 1); - if (!inflight) - backend->remove(evpid); - else { - backend->delete(evpid); - ninflight -= 1; - stat_decrement("scheduler.envelope.inflight", 1); - } - - scheduler_reset_events(); - return; - - case IMSG_QUEUE_ENVELOPE_ACK: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: queue ack removal of evp:%016" PRIx64, - evpid); - ninflight -= 1; - stat_decrement("scheduler.envelope.inflight", 1); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_DELIVERY_OK: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: deleting evp:%016" PRIx64 " (ok)", evpid); - backend->delete(evpid); - ninflight -= 1; - stat_increment("scheduler.delivery.ok", 1); - stat_decrement("scheduler.envelope.inflight", 1); - stat_decrement("scheduler.envelope", 1); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_DELIVERY_TEMPFAIL: - m_msg(&m, imsg); - m_get_envelope(&m, &evp); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: updating evp:%016" PRIx64, evp.id); - scheduler_info(&si, &evp); - backend->update(&si); - ninflight -= 1; - stat_increment("scheduler.delivery.tempfail", 1); - stat_decrement("scheduler.envelope.inflight", 1); - - for (i = 0; i < MAX_BOUNCE_WARN; i++) { - if (env->sc_bounce_warn[i] == 0) - break; - timestamp = si.creation + env->sc_bounce_warn[i]; - if (si.nexttry >= timestamp && - si.lastbounce < timestamp) { - req.evpid = evp.id; - req.timestamp = timestamp; - req.bounce.type = B_DELAYED; - req.bounce.delay = env->sc_bounce_warn[i]; - req.bounce.ttl = si.ttl; - m_compose(p, IMSG_SCHED_ENVELOPE_BOUNCE, 0, 0, -1, - &req, sizeof req); - break; - } - } - scheduler_reset_events(); - return; - - case IMSG_QUEUE_DELIVERY_PERMFAIL: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: deleting evp:%016" PRIx64 " (fail)", evpid); - backend->delete(evpid); - ninflight -= 1; - stat_increment("scheduler.delivery.permfail", 1); - stat_decrement("scheduler.envelope.inflight", 1); - stat_decrement("scheduler.envelope", 1); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_DELIVERY_LOOP: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: deleting evp:%016" PRIx64 " (loop)", evpid); - backend->delete(evpid); - ninflight -= 1; - stat_increment("scheduler.delivery.loop", 1); - stat_decrement("scheduler.envelope.inflight", 1); - stat_decrement("scheduler.envelope", 1); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_HOLDQ_HOLD: - m_msg(&m, imsg); - m_get_evpid(&m, &evpid); - m_get_id(&m, &holdq); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: holding evp:%016" PRIx64 " on %016" PRIx64, - evpid, holdq); - backend->hold(evpid, holdq); - ninflight -= 1; - stat_decrement("scheduler.envelope.inflight", 1); - scheduler_reset_events(); - return; - - case IMSG_QUEUE_HOLDQ_RELEASE: - m_msg(&m, imsg); - m_get_int(&m, &type); - m_get_id(&m, &holdq); - m_get_int(&m, &r); - m_end(&m); - log_trace(TRACE_SCHEDULER, - "scheduler: releasing %d on holdq (%d, %016" PRIx64 ")", - r, type, holdq); - backend->release(type, holdq, r); - scheduler_reset_events(); - return; - - case IMSG_CTL_PAUSE_MDA: - log_trace(TRACE_SCHEDULER, "scheduler: pausing mda"); - env->sc_flags |= SMTPD_MDA_PAUSED; - return; - - case IMSG_CTL_RESUME_MDA: - log_trace(TRACE_SCHEDULER, "scheduler: resuming mda"); - env->sc_flags &= ~SMTPD_MDA_PAUSED; - scheduler_reset_events(); - return; - - case IMSG_CTL_PAUSE_MTA: - log_trace(TRACE_SCHEDULER, "scheduler: pausing mta"); - env->sc_flags |= SMTPD_MTA_PAUSED; - return; - - case IMSG_CTL_RESUME_MTA: - log_trace(TRACE_SCHEDULER, "scheduler: resuming mta"); - env->sc_flags &= ~SMTPD_MTA_PAUSED; - scheduler_reset_events(); - return; - - case IMSG_CTL_VERBOSE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - log_setverbose(v); - return; - - case IMSG_CTL_PROFILE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - profiling = v; - return; - - case IMSG_CTL_LIST_MESSAGES: - msgid = *(uint32_t *)(imsg->data); - n = backend->messages(msgid, msgids, env->sc_scheduler_max_msg_batch_size); - m_compose(p, IMSG_CTL_LIST_MESSAGES, imsg->hdr.peerid, 0, -1, - msgids, n * sizeof (*msgids)); - return; - - case IMSG_CTL_LIST_ENVELOPES: - id = *(uint64_t *)(imsg->data); - n = backend->envelopes(id, state, env->sc_scheduler_max_evp_batch_size); - for (i = 0; i < n; i++) { - m_create(p_queue, IMSG_CTL_LIST_ENVELOPES, - imsg->hdr.peerid, 0, -1); - m_add_evpid(p_queue, state[i].evpid); - m_add_int(p_queue, state[i].flags); - m_add_time(p_queue, state[i].time); - m_close(p_queue); - } - m_compose(p_queue, IMSG_CTL_LIST_ENVELOPES, - imsg->hdr.peerid, 0, -1, NULL, 0); - return; - - case IMSG_CTL_SCHEDULE: - id = *(uint64_t *)(imsg->data); - if (id <= 0xffffffffL) - log_debug("debug: scheduler: " - "scheduling msg:%08" PRIx64, id); - else - log_debug("debug: scheduler: " - "scheduling evp:%016" PRIx64, id); - r = backend->schedule(id); - scheduler_reset_events(); - m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_QUEUE_ENVELOPE_SCHEDULE: - id = *(uint64_t *)(imsg->data); - backend->schedule(id); - scheduler_reset_events(); - return; - - case IMSG_CTL_REMOVE: - id = *(uint64_t *)(imsg->data); - if (id <= 0xffffffffL) - log_debug("debug: scheduler: " - "removing msg:%08" PRIx64, id); - else - log_debug("debug: scheduler: " - "removing evp:%016" PRIx64, id); - r = backend->remove(id); - scheduler_reset_events(); - m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_CTL_PAUSE_EVP: - id = *(uint64_t *)(imsg->data); - if (id <= 0xffffffffL) - log_debug("debug: scheduler: " - "suspending msg:%08" PRIx64, id); - else - log_debug("debug: scheduler: " - "suspending evp:%016" PRIx64, id); - r = backend->suspend(id); - scheduler_reset_events(); - m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - - case IMSG_CTL_RESUME_EVP: - id = *(uint64_t *)(imsg->data); - if (id <= 0xffffffffL) - log_debug("debug: scheduler: " - "resuming msg:%08" PRIx64, id); - else - log_debug("debug: scheduler: " - "resuming evp:%016" PRIx64, id); - r = backend->resume(id); - scheduler_reset_events(); - m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid, - 0, -1, NULL, 0); - return; - } - - errx(1, "scheduler_imsg: unexpected %s imsg", - imsg_to_str(imsg->hdr.type)); -} - -static void -scheduler_shutdown(void) -{ - log_debug("debug: scheduler agent exiting"); - _exit(0); -} - -static void -scheduler_reset_events(void) -{ - struct timeval tv; - - evtimer_del(&ev); - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&ev, &tv); -} - -int -scheduler(void) -{ - struct passwd *pw; - - backend = scheduler_backend_lookup(backend_scheduler); - if (backend == NULL) - errx(1, "cannot find scheduler backend \"%s\"", - backend_scheduler); - - purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS); - - if ((pw = getpwnam(SMTPD_USER)) == NULL) - fatalx("unknown user " SMTPD_USER); - - config_process(PROC_SCHEDULER); - - backend->init(backend_scheduler); - - if (chroot(PATH_CHROOT) == -1) - fatal("scheduler: chroot"); - if (chdir("/") == -1) - fatal("scheduler: chdir(\"/\")"); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("scheduler: cannot drop privileges"); - - evpids = xcalloc(env->sc_scheduler_max_schedule, sizeof *evpids); - types = xcalloc(env->sc_scheduler_max_schedule, sizeof *types); - msgids = xcalloc(env->sc_scheduler_max_msg_batch_size, sizeof *msgids); - state = xcalloc(env->sc_scheduler_max_evp_batch_size, sizeof *state); - - imsg_callback = scheduler_imsg; - event_init(); - - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); - - config_peer(PROC_CONTROL); - config_peer(PROC_QUEUE); - - evtimer_set(&ev, scheduler_timeout, NULL); - scheduler_reset_events(); - -#if HAVE_PLEDGE - if (pledge("stdio", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} - -static void -scheduler_timeout(int fd, short event, void *p) -{ - struct timeval tv; - size_t i; - size_t d_inflight; - size_t d_envelope; - size_t d_removed; - size_t d_expired; - size_t d_updated; - size_t count; - int mask, r, delay; - - tv.tv_sec = 0; - tv.tv_usec = 0; - - mask = SCHED_UPDATE; - - if (ninflight < env->sc_scheduler_max_inflight) { - mask |= SCHED_EXPIRE | SCHED_REMOVE | SCHED_BOUNCE; - if (!(env->sc_flags & SMTPD_MDA_PAUSED)) - mask |= SCHED_MDA; - if (!(env->sc_flags & SMTPD_MTA_PAUSED)) - mask |= SCHED_MTA; - } - - count = env->sc_scheduler_max_schedule; - - log_trace(TRACE_SCHEDULER, "scheduler: getting batch: mask=0x%x, count=%zu", mask, count); - - r = backend->batch(mask, &delay, &count, evpids, types); - - log_trace(TRACE_SCHEDULER, "scheduler: got r=%i, delay=%i, count=%zu", r, delay, count); - - if (r < 0) - fatalx("scheduler: error in batch handler"); - - if (r == 0) { - - if (delay < -1) - fatalx("scheduler: invalid delay %d", delay); - - if (delay == -1) { - log_trace(TRACE_SCHEDULER, "scheduler: sleeping"); - return; - } - - tv.tv_sec = delay; - tv.tv_usec = 0; - log_trace(TRACE_SCHEDULER, - "scheduler: waiting for %s", duration_to_text(tv.tv_sec)); - evtimer_add(&ev, &tv); - return; - } - - d_inflight = 0; - d_envelope = 0; - d_removed = 0; - d_expired = 0; - d_updated = 0; - - for (i = 0; i < count; i++) { - switch(types[i]) { - case SCHED_REMOVE: - log_debug("debug: scheduler: evp:%016" PRIx64 - " removed", evpids[i]); - m_create(p_queue, IMSG_SCHED_ENVELOPE_REMOVE, 0, 0, -1); - m_add_evpid(p_queue, evpids[i]); - m_close(p_queue); - d_envelope += 1; - d_removed += 1; - d_inflight += 1; - break; - - case SCHED_EXPIRE: - log_debug("debug: scheduler: evp:%016" PRIx64 - " expired", evpids[i]); - m_create(p_queue, IMSG_SCHED_ENVELOPE_EXPIRE, 0, 0, -1); - m_add_evpid(p_queue, evpids[i]); - m_close(p_queue); - d_envelope += 1; - d_expired += 1; - d_inflight += 1; - break; - - case SCHED_UPDATE: - log_debug("debug: scheduler: evp:%016" PRIx64 - " scheduled (update)", evpids[i]); - d_updated += 1; - break; - - case SCHED_BOUNCE: - log_debug("debug: scheduler: evp:%016" PRIx64 - " scheduled (bounce)", evpids[i]); - m_create(p_queue, IMSG_SCHED_ENVELOPE_INJECT, 0, 0, -1); - m_add_evpid(p_queue, evpids[i]); - m_close(p_queue); - d_inflight += 1; - break; - - case SCHED_MDA: - log_debug("debug: scheduler: evp:%016" PRIx64 - " scheduled (mda)", evpids[i]); - m_create(p_queue, IMSG_SCHED_ENVELOPE_DELIVER, 0, 0, -1); - m_add_evpid(p_queue, evpids[i]); - m_close(p_queue); - d_inflight += 1; - break; - - case SCHED_MTA: - log_debug("debug: scheduler: evp:%016" PRIx64 - " scheduled (mta)", evpids[i]); - m_create(p_queue, IMSG_SCHED_ENVELOPE_TRANSFER, 0, 0, -1); - m_add_evpid(p_queue, evpids[i]); - m_close(p_queue); - d_inflight += 1; - break; - } - } - - stat_decrement("scheduler.envelope", d_envelope); - stat_increment("scheduler.envelope.inflight", d_inflight); - stat_increment("scheduler.envelope.expired", d_expired); - stat_increment("scheduler.envelope.removed", d_removed); - stat_increment("scheduler.envelope.updated", d_updated); - - ninflight += d_inflight; - - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&ev, &tv); -} diff --git a/smtpd/scheduler_backend.c b/smtpd/scheduler_backend.c deleted file mode 100644 index ad2b4cab..00000000 --- a/smtpd/scheduler_backend.c +++ /dev/null @@ -1,82 +0,0 @@ -/* $OpenBSD: scheduler_backend.c,v 1.16 2018/05/24 11:38:24 gilles Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -extern struct scheduler_backend scheduler_backend_null; -extern struct scheduler_backend scheduler_backend_proc; -extern struct scheduler_backend scheduler_backend_ramqueue; - -struct scheduler_backend * -scheduler_backend_lookup(const char *name) -{ - if (!strcmp(name, "null")) - return &scheduler_backend_null; - if (!strcmp(name, "ramqueue")) - return &scheduler_backend_ramqueue; - - return &scheduler_backend_proc; -} - -void -scheduler_info(struct scheduler_info *sched, struct envelope *evp) -{ - struct dispatcher *disp; - - disp = evp->type == D_BOUNCE ? - env->sc_dispatcher_bounce : - dict_xget(env->sc_dispatchers, evp->dispatcher); - - switch (disp->type) { - case DISPATCHER_LOCAL: - sched->type = D_MDA; - break; - case DISPATCHER_REMOTE: - sched->type = D_MTA; - break; - case DISPATCHER_BOUNCE: - sched->type = D_BOUNCE; - break; - } - sched->ttl = disp->ttl ? disp->ttl : env->sc_ttl; - - sched->evpid = evp->id; - sched->creation = evp->creation; - sched->retry = evp->retry; - sched->lasttry = evp->lasttry; - sched->lastbounce = evp->lastbounce; - sched->nexttry = 0; -} diff --git a/smtpd/scheduler_null.c b/smtpd/scheduler_null.c deleted file mode 100644 index 40db6205..00000000 --- a/smtpd/scheduler_null.c +++ /dev/null @@ -1,164 +0,0 @@ -/* $OpenBSD: scheduler_null.c,v 1.9 2015/01/20 17:37:54 deraadt Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <limits.h> - -#include "smtpd.h" - -static int scheduler_null_init(const char *); -static int scheduler_null_insert(struct scheduler_info *); -static size_t scheduler_null_commit(uint32_t); -static size_t scheduler_null_rollback(uint32_t); -static int scheduler_null_update(struct scheduler_info *); -static int scheduler_null_delete(uint64_t); -static int scheduler_null_hold(uint64_t, uint64_t); -static int scheduler_null_release(int, uint64_t, int); -static int scheduler_null_batch(int, int*, size_t*, uint64_t*, int*); -static size_t scheduler_null_messages(uint32_t, uint32_t *, size_t); -static size_t scheduler_null_envelopes(uint64_t, struct evpstate *, size_t); -static int scheduler_null_schedule(uint64_t); -static int scheduler_null_remove(uint64_t); -static int scheduler_null_suspend(uint64_t); -static int scheduler_null_resume(uint64_t); - -struct scheduler_backend scheduler_backend_null = { - scheduler_null_init, - - scheduler_null_insert, - scheduler_null_commit, - scheduler_null_rollback, - - scheduler_null_update, - scheduler_null_delete, - scheduler_null_hold, - scheduler_null_release, - - scheduler_null_batch, - - scheduler_null_messages, - scheduler_null_envelopes, - scheduler_null_schedule, - scheduler_null_remove, - scheduler_null_suspend, - scheduler_null_resume, -}; - -static int -scheduler_null_init(const char *arg) -{ - return (1); -} - -static int -scheduler_null_insert(struct scheduler_info *si) -{ - return (0); -} - -static size_t -scheduler_null_commit(uint32_t msgid) -{ - return (0); -} - -static size_t -scheduler_null_rollback(uint32_t msgid) -{ - return (0); -} - -static int -scheduler_null_update(struct scheduler_info *si) -{ - return (0); -} - -static int -scheduler_null_delete(uint64_t evpid) -{ - return (0); -} - -static int -scheduler_null_hold(uint64_t evpid, uint64_t holdq) -{ - return (0); -} - -static int -scheduler_null_release(int type, uint64_t holdq, int n) -{ - return (0); -} - -static int -scheduler_null_batch(int typemask, int *delay, size_t *count, uint64_t *evpids, int *types) -{ - *delay = 0; - - return (0); -} - -static int -scheduler_null_schedule(uint64_t evpid) -{ - return (0); -} - -static int -scheduler_null_remove(uint64_t evpid) -{ - return (0); -} - -static int -scheduler_null_suspend(uint64_t evpid) -{ - return (0); -} - -static int -scheduler_null_resume(uint64_t evpid) -{ - return (0); -} - -static size_t -scheduler_null_messages(uint32_t from, uint32_t *dst, size_t size) -{ - return (0); -} - -static size_t -scheduler_null_envelopes(uint64_t from, struct evpstate *dst, size_t size) -{ - return (0); -} diff --git a/smtpd/scheduler_proc.c b/smtpd/scheduler_proc.c deleted file mode 100644 index 5f4e8b70..00000000 --- a/smtpd/scheduler_proc.c +++ /dev/null @@ -1,446 +0,0 @@ -/* $OpenBSD: scheduler_proc.c,v 1.8 2015/12/05 13:14:21 claudio Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -static struct imsgbuf ibuf; -static struct imsg imsg; -static size_t rlen; -static char *rdata; - -static void -scheduler_proc_call(void) -{ - ssize_t n; - - if (imsg_flush(&ibuf) == -1) { - log_warn("warn: scheduler-proc: imsg_flush"); - fatalx("scheduler-proc: exiting"); - } - - while (1) { - if ((n = imsg_get(&ibuf, &imsg)) == -1) { - log_warn("warn: scheduler-proc: imsg_get"); - break; - } - if (n) { - rlen = imsg.hdr.len - IMSG_HEADER_SIZE; - rdata = imsg.data; - - if (imsg.hdr.type != PROC_SCHEDULER_OK) { - log_warnx("warn: scheduler-proc: bad response"); - break; - } - return; - } - - if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN) { - log_warn("warn: scheduler-proc: imsg_read"); - break; - } - - if (n == 0) { - log_warnx("warn: scheduler-proc: pipe closed"); - break; - } - } - - fatalx("scheduler-proc: exiting"); -} - -static void -scheduler_proc_read(void *dst, size_t len) -{ - if (len > rlen) { - log_warnx("warn: scheduler-proc: bad msg len"); - fatalx("scheduler-proc: exiting"); - } - - memmove(dst, rdata, len); - rlen -= len; - rdata += len; -} - -static void -scheduler_proc_end(void) -{ - if (rlen) { - log_warnx("warn: scheduler-proc: bogus data"); - fatalx("scheduler-proc: exiting"); - } - imsg_free(&imsg); -} - -/* - * API - */ - -static int -scheduler_proc_init(const char *conf) -{ - int fd, r; - uint32_t version; - - fd = fork_proc_backend("scheduler", conf, "scheduler-proc"); - if (fd == -1) - fatalx("scheduler-proc: exiting"); - - imsg_init(&ibuf, fd); - - version = PROC_SCHEDULER_API_VERSION; - imsg_compose(&ibuf, PROC_SCHEDULER_INIT, 0, 0, -1, - &version, sizeof(version)); - scheduler_proc_call(); - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (1); -} - -static int -scheduler_proc_insert(struct scheduler_info *si) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_INSERT"); - - imsg_compose(&ibuf, PROC_SCHEDULER_INSERT, 0, 0, -1, si, sizeof(*si)); - - scheduler_proc_call(); - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static size_t -scheduler_proc_commit(uint32_t msgid) -{ - size_t s; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_COMMIT"); - - imsg_compose(&ibuf, PROC_SCHEDULER_COMMIT, 0, 0, -1, - &msgid, sizeof(msgid)); - - scheduler_proc_call(); - scheduler_proc_read(&s, sizeof(s)); - scheduler_proc_end(); - - return (s); -} - -static size_t -scheduler_proc_rollback(uint32_t msgid) -{ - size_t s; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_ROLLBACK"); - - imsg_compose(&ibuf, PROC_SCHEDULER_ROLLBACK, 0, 0, -1, - &msgid, sizeof(msgid)); - - scheduler_proc_call(); - scheduler_proc_read(&s, sizeof(s)); - scheduler_proc_end(); - - return (s); -} - -static int -scheduler_proc_update(struct scheduler_info *si) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_UPDATE"); - - imsg_compose(&ibuf, PROC_SCHEDULER_UPDATE, 0, 0, -1, si, sizeof(*si)); - - scheduler_proc_call(); - scheduler_proc_read(&r, sizeof(r)); - if (r == 1) - scheduler_proc_read(si, sizeof(*si)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_delete(uint64_t evpid) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_DELETE"); - - imsg_compose(&ibuf, PROC_SCHEDULER_DELETE, 0, 0, -1, - &evpid, sizeof(evpid)); - - scheduler_proc_call(); - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_hold(uint64_t evpid, uint64_t holdq) -{ - struct ibuf *buf; - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_HOLD"); - - buf = imsg_create(&ibuf, PROC_SCHEDULER_HOLD, 0, 0, - sizeof(evpid) + sizeof(holdq)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &evpid, sizeof(evpid)) == -1) - return (-1); - if (imsg_add(buf, &holdq, sizeof(holdq)) == -1) - return (-1); - imsg_close(&ibuf, buf); - - scheduler_proc_call(); - - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_release(int type, uint64_t holdq, int n) -{ - struct ibuf *buf; - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_RELEASE"); - - buf = imsg_create(&ibuf, PROC_SCHEDULER_RELEASE, 0, 0, - sizeof(holdq) + sizeof(n)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &type, sizeof(type)) == -1) - return (-1); - if (imsg_add(buf, &holdq, sizeof(holdq)) == -1) - return (-1); - if (imsg_add(buf, &n, sizeof(n)) == -1) - return (-1); - imsg_close(&ibuf, buf); - - scheduler_proc_call(); - - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_batch(int typemask, int *delay, size_t *count, uint64_t *evpids, int *types) -{ - struct ibuf *buf; - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_BATCH"); - - buf = imsg_create(&ibuf, PROC_SCHEDULER_BATCH, 0, 0, - sizeof(typemask) + sizeof(*count)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &typemask, sizeof(typemask)) == -1) - return (-1); - if (imsg_add(buf, count, sizeof(*count)) == -1) - return (-1); - imsg_close(&ibuf, buf); - - scheduler_proc_call(); - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_read(delay, sizeof(*delay)); - scheduler_proc_read(count, sizeof(*count)); - if (r > 0) { - scheduler_proc_read(evpids, sizeof(*evpids) * (*count)); - scheduler_proc_read(types, sizeof(*types) * (*count)); - } - scheduler_proc_end(); - - return (r); -} - -static size_t -scheduler_proc_messages(uint32_t from, uint32_t *dst, size_t size) -{ - struct ibuf *buf; - size_t s; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_MESSAGES"); - - buf = imsg_create(&ibuf, PROC_SCHEDULER_MESSAGES, 0, 0, - sizeof(from) + sizeof(size)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &from, sizeof(from)) == -1) - return (-1); - if (imsg_add(buf, &size, sizeof(size)) == -1) - return (-1); - imsg_close(&ibuf, buf); - - scheduler_proc_call(); - - s = rlen / sizeof(*dst); - scheduler_proc_read(dst, s * sizeof(*dst)); - scheduler_proc_end(); - - return (s); -} - -static size_t -scheduler_proc_envelopes(uint64_t from, struct evpstate *dst, size_t size) -{ - struct ibuf *buf; - size_t s; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_ENVELOPES"); - - buf = imsg_create(&ibuf, PROC_SCHEDULER_ENVELOPES, 0, 0, - sizeof(from) + sizeof(size)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &from, sizeof(from)) == -1) - return (-1); - if (imsg_add(buf, &size, sizeof(size)) == -1) - return (-1); - imsg_close(&ibuf, buf); - - scheduler_proc_call(); - - s = rlen / sizeof(*dst); - scheduler_proc_read(dst, s * sizeof(*dst)); - scheduler_proc_end(); - - return (s); -} - -static int -scheduler_proc_schedule(uint64_t evpid) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_SCHEDULE"); - - imsg_compose(&ibuf, PROC_SCHEDULER_SCHEDULE, 0, 0, -1, - &evpid, sizeof(evpid)); - - scheduler_proc_call(); - - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_remove(uint64_t evpid) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_REMOVE"); - - imsg_compose(&ibuf, PROC_SCHEDULER_REMOVE, 0, 0, -1, - &evpid, sizeof(evpid)); - - scheduler_proc_call(); - - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_suspend(uint64_t evpid) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_SUSPEND"); - - imsg_compose(&ibuf, PROC_SCHEDULER_SUSPEND, 0, 0, -1, - &evpid, sizeof(evpid)); - - scheduler_proc_call(); - - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -static int -scheduler_proc_resume(uint64_t evpid) -{ - int r; - - log_debug("debug: scheduler-proc: PROC_SCHEDULER_RESUME"); - - imsg_compose(&ibuf, PROC_SCHEDULER_RESUME, 0, 0, -1, - &evpid, sizeof(evpid)); - - scheduler_proc_call(); - - scheduler_proc_read(&r, sizeof(r)); - scheduler_proc_end(); - - return (r); -} - -struct scheduler_backend scheduler_backend_proc = { - scheduler_proc_init, - scheduler_proc_insert, - scheduler_proc_commit, - scheduler_proc_rollback, - scheduler_proc_update, - scheduler_proc_delete, - scheduler_proc_hold, - scheduler_proc_release, - scheduler_proc_batch, - scheduler_proc_messages, - scheduler_proc_envelopes, - scheduler_proc_schedule, - scheduler_proc_remove, - scheduler_proc_suspend, - scheduler_proc_resume, -}; diff --git a/smtpd/scheduler_ramqueue.c b/smtpd/scheduler_ramqueue.c deleted file mode 100644 index 0c04fc0b..00000000 --- a/smtpd/scheduler_ramqueue.c +++ /dev/null @@ -1,1204 +0,0 @@ -/* $OpenBSD: scheduler_ramqueue.c,v 1.45 2018/05/31 21:06:12 gilles Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <time.h> - -#include "smtpd.h" -#include "log.h" - -TAILQ_HEAD(evplist, rq_envelope); - -struct rq_message { - uint32_t msgid; - struct tree envelopes; -}; - -struct rq_envelope { - TAILQ_ENTRY(rq_envelope) entry; - SPLAY_ENTRY(rq_envelope) t_entry; - - uint64_t evpid; - uint64_t holdq; - enum delivery_type type; - -#define RQ_EVPSTATE_PENDING 0 -#define RQ_EVPSTATE_SCHEDULED 1 -#define RQ_EVPSTATE_INFLIGHT 2 -#define RQ_EVPSTATE_HELD 3 - uint8_t state; - -#define RQ_ENVELOPE_EXPIRED 0x01 -#define RQ_ENVELOPE_REMOVED 0x02 -#define RQ_ENVELOPE_SUSPEND 0x04 -#define RQ_ENVELOPE_UPDATE 0x08 -#define RQ_ENVELOPE_OVERFLOW 0x10 - uint8_t flags; - - time_t ctime; - time_t sched; - time_t expire; - - struct rq_message *message; - - time_t t_inflight; - time_t t_scheduled; -}; - -struct rq_holdq { - struct evplist q; - size_t count; -}; - -struct rq_queue { - size_t evpcount; - struct tree messages; - SPLAY_HEAD(prioqtree, rq_envelope) q_priotree; - - struct evplist q_pending; - struct evplist q_inflight; - - struct evplist q_mta; - struct evplist q_mda; - struct evplist q_bounce; - struct evplist q_update; - struct evplist q_expired; - struct evplist q_removed; -}; - -static int rq_envelope_cmp(struct rq_envelope *, struct rq_envelope *); - -SPLAY_PROTOTYPE(prioqtree, rq_envelope, t_entry, rq_envelope_cmp); -static int scheduler_ram_init(const char *); -static int scheduler_ram_insert(struct scheduler_info *); -static size_t scheduler_ram_commit(uint32_t); -static size_t scheduler_ram_rollback(uint32_t); -static int scheduler_ram_update(struct scheduler_info *); -static int scheduler_ram_delete(uint64_t); -static int scheduler_ram_hold(uint64_t, uint64_t); -static int scheduler_ram_release(int, uint64_t, int); -static int scheduler_ram_batch(int, int *, size_t *, uint64_t *, int *); -static size_t scheduler_ram_messages(uint32_t, uint32_t *, size_t); -static size_t scheduler_ram_envelopes(uint64_t, struct evpstate *, size_t); -static int scheduler_ram_schedule(uint64_t); -static int scheduler_ram_remove(uint64_t); -static int scheduler_ram_suspend(uint64_t); -static int scheduler_ram_resume(uint64_t); -static int scheduler_ram_query(uint64_t); - -static void sorted_insert(struct rq_queue *, struct rq_envelope *); - -static void rq_queue_init(struct rq_queue *); -static void rq_queue_merge(struct rq_queue *, struct rq_queue *); -static void rq_queue_dump(struct rq_queue *, const char *); -static void rq_queue_schedule(struct rq_queue *rq); -static struct evplist *rq_envelope_list(struct rq_queue *, struct rq_envelope *); -static void rq_envelope_schedule(struct rq_queue *, struct rq_envelope *); -static int rq_envelope_remove(struct rq_queue *, struct rq_envelope *); -static int rq_envelope_suspend(struct rq_queue *, struct rq_envelope *); -static int rq_envelope_resume(struct rq_queue *, struct rq_envelope *); -static void rq_envelope_delete(struct rq_queue *, struct rq_envelope *); -static const char *rq_envelope_to_text(struct rq_envelope *); - -struct scheduler_backend scheduler_backend_ramqueue = { - scheduler_ram_init, - - scheduler_ram_insert, - scheduler_ram_commit, - scheduler_ram_rollback, - - scheduler_ram_update, - scheduler_ram_delete, - scheduler_ram_hold, - scheduler_ram_release, - - scheduler_ram_batch, - - scheduler_ram_messages, - scheduler_ram_envelopes, - scheduler_ram_schedule, - scheduler_ram_remove, - scheduler_ram_suspend, - scheduler_ram_resume, - scheduler_ram_query, -}; - -static struct rq_queue ramqueue; -static struct tree updates; -static struct tree holdqs[3]; /* delivery type */ - -static time_t currtime; - -#define BACKOFF_TRANSFER 400 -#define BACKOFF_DELIVERY 10 -#define BACKOFF_OVERFLOW 3 - -static time_t -scheduler_backoff(time_t t0, time_t base, uint32_t step) -{ - return (t0 + base * step * step); -} - -static time_t -scheduler_next(time_t t0, time_t base, uint32_t step) -{ - time_t t; - - /* XXX be more efficient */ - while ((t = scheduler_backoff(t0, base, step)) <= currtime) - step++; - - return (t); -} - -static int -scheduler_ram_init(const char *arg) -{ - rq_queue_init(&ramqueue); - tree_init(&updates); - tree_init(&holdqs[D_MDA]); - tree_init(&holdqs[D_MTA]); - tree_init(&holdqs[D_BOUNCE]); - - return (1); -} - -static int -scheduler_ram_insert(struct scheduler_info *si) -{ - struct rq_queue *update; - struct rq_message *message; - struct rq_envelope *envelope; - uint32_t msgid; - - currtime = time(NULL); - - msgid = evpid_to_msgid(si->evpid); - - /* find/prepare a ramqueue update */ - if ((update = tree_get(&updates, msgid)) == NULL) { - update = xcalloc(1, sizeof *update); - stat_increment("scheduler.ramqueue.update", 1); - rq_queue_init(update); - tree_xset(&updates, msgid, update); - } - - /* find/prepare the msgtree message in ramqueue update */ - if ((message = tree_get(&update->messages, msgid)) == NULL) { - message = xcalloc(1, sizeof *message); - message->msgid = msgid; - tree_init(&message->envelopes); - tree_xset(&update->messages, msgid, message); - stat_increment("scheduler.ramqueue.message", 1); - } - - /* create envelope in ramqueue message */ - envelope = xcalloc(1, sizeof *envelope); - envelope->evpid = si->evpid; - envelope->type = si->type; - envelope->message = message; - envelope->ctime = si->creation; - envelope->expire = si->creation + si->ttl; - envelope->sched = scheduler_backoff(si->creation, - (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry); - tree_xset(&message->envelopes, envelope->evpid, envelope); - - update->evpcount++; - stat_increment("scheduler.ramqueue.envelope", 1); - - envelope->state = RQ_EVPSTATE_PENDING; - TAILQ_INSERT_TAIL(&update->q_pending, envelope, entry); - - si->nexttry = envelope->sched; - - return (1); -} - -static size_t -scheduler_ram_commit(uint32_t msgid) -{ - struct rq_queue *update; - size_t r; - - currtime = time(NULL); - - update = tree_xpop(&updates, msgid); - r = update->evpcount; - - if (tracing & TRACE_SCHEDULER) - rq_queue_dump(update, "update to commit"); - - rq_queue_merge(&ramqueue, update); - - if (tracing & TRACE_SCHEDULER) - rq_queue_dump(&ramqueue, "resulting queue"); - - rq_queue_schedule(&ramqueue); - - free(update); - stat_decrement("scheduler.ramqueue.update", 1); - - return (r); -} - -static size_t -scheduler_ram_rollback(uint32_t msgid) -{ - struct rq_queue *update; - struct rq_envelope *evp; - size_t r; - - currtime = time(NULL); - - if ((update = tree_pop(&updates, msgid)) == NULL) - return (0); - r = update->evpcount; - - while ((evp = TAILQ_FIRST(&update->q_pending))) { - TAILQ_REMOVE(&update->q_pending, evp, entry); - rq_envelope_delete(update, evp); - } - - free(update); - stat_decrement("scheduler.ramqueue.update", 1); - - return (r); -} - -static int -scheduler_ram_update(struct scheduler_info *si) -{ - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - - currtime = time(NULL); - - msgid = evpid_to_msgid(si->evpid); - msg = tree_xget(&ramqueue.messages, msgid); - evp = tree_xget(&msg->envelopes, si->evpid); - - /* it *must* be in-flight */ - if (evp->state != RQ_EVPSTATE_INFLIGHT) - errx(1, "evp:%016" PRIx64 " not in-flight", si->evpid); - - TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry); - - /* - * If the envelope was removed while inflight, schedule it for - * removal immediately. - */ - if (evp->flags & RQ_ENVELOPE_REMOVED) { - TAILQ_INSERT_TAIL(&ramqueue.q_removed, evp, entry); - evp->state = RQ_EVPSTATE_SCHEDULED; - evp->t_scheduled = currtime; - return (1); - } - - evp->sched = scheduler_next(evp->ctime, - (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry); - - evp->state = RQ_EVPSTATE_PENDING; - if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) - sorted_insert(&ramqueue, evp); - - si->nexttry = evp->sched; - - return (1); -} - -static int -scheduler_ram_delete(uint64_t evpid) -{ - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - - currtime = time(NULL); - - msgid = evpid_to_msgid(evpid); - msg = tree_xget(&ramqueue.messages, msgid); - evp = tree_xget(&msg->envelopes, evpid); - - /* it *must* be in-flight */ - if (evp->state != RQ_EVPSTATE_INFLIGHT) - errx(1, "evp:%016" PRIx64 " not in-flight", evpid); - - TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry); - - rq_envelope_delete(&ramqueue, evp); - - return (1); -} - -#define HOLDQ_MAXSIZE 1000 - -static int -scheduler_ram_hold(uint64_t evpid, uint64_t holdq) -{ - struct rq_holdq *hq; - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - - currtime = time(NULL); - - msgid = evpid_to_msgid(evpid); - msg = tree_xget(&ramqueue.messages, msgid); - evp = tree_xget(&msg->envelopes, evpid); - - /* it *must* be in-flight */ - if (evp->state != RQ_EVPSTATE_INFLIGHT) - errx(1, "evp:%016" PRIx64 " not in-flight", evpid); - - TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry); - - /* If the envelope is suspended, just mark it as pending */ - if (evp->flags & RQ_ENVELOPE_SUSPEND) { - evp->state = RQ_EVPSTATE_PENDING; - return (0); - } - - hq = tree_get(&holdqs[evp->type], holdq); - if (hq == NULL) { - hq = xcalloc(1, sizeof(*hq)); - TAILQ_INIT(&hq->q); - tree_xset(&holdqs[evp->type], holdq, hq); - stat_increment("scheduler.ramqueue.holdq", 1); - } - - /* If the holdq is full, just "tempfail" the envelope */ - if (hq->count >= HOLDQ_MAXSIZE) { - evp->state = RQ_EVPSTATE_PENDING; - evp->flags |= RQ_ENVELOPE_UPDATE; - evp->flags |= RQ_ENVELOPE_OVERFLOW; - sorted_insert(&ramqueue, evp); - stat_increment("scheduler.ramqueue.hold-overflow", 1); - return (0); - } - - evp->state = RQ_EVPSTATE_HELD; - evp->holdq = holdq; - /* This is an optimization: upon release, the envelopes will be - * inserted in the pending queue from the first element to the last. - * Since elements already in the queue were received first, they - * were scheduled first, so they will be reinserted before the - * current element. - */ - TAILQ_INSERT_HEAD(&hq->q, evp, entry); - hq->count += 1; - stat_increment("scheduler.ramqueue.hold", 1); - - return (1); -} - -static int -scheduler_ram_release(int type, uint64_t holdq, int n) -{ - struct rq_holdq *hq; - struct rq_envelope *evp; - int i, update; - - currtime = time(NULL); - - hq = tree_get(&holdqs[type], holdq); - if (hq == NULL) - return (0); - - if (n == -1) { - n = 0; - update = 1; - } - else - update = 0; - - for (i = 0; n == 0 || i < n; i++) { - evp = TAILQ_FIRST(&hq->q); - if (evp == NULL) - break; - - TAILQ_REMOVE(&hq->q, evp, entry); - hq->count -= 1; - evp->holdq = 0; - - /* When released, all envelopes are put in the pending queue - * and will be rescheduled immediately. As an optimization, - * we could just schedule them directly. - */ - evp->state = RQ_EVPSTATE_PENDING; - if (update) - evp->flags |= RQ_ENVELOPE_UPDATE; - sorted_insert(&ramqueue, evp); - } - - if (TAILQ_EMPTY(&hq->q)) { - tree_xpop(&holdqs[type], holdq); - free(hq); - stat_decrement("scheduler.ramqueue.holdq", 1); - } - stat_decrement("scheduler.ramqueue.hold", i); - - return (i); -} - -static int -scheduler_ram_batch(int mask, int *delay, size_t *count, uint64_t *evpids, int *types) -{ - struct rq_envelope *evp; - size_t i, n; - time_t t; - - currtime = time(NULL); - - rq_queue_schedule(&ramqueue); - if (tracing & TRACE_SCHEDULER) - rq_queue_dump(&ramqueue, "scheduler_ram_batch()"); - - i = 0; - n = 0; - - for (;;) { - - if (mask & SCHED_REMOVE && (evp = TAILQ_FIRST(&ramqueue.q_removed))) { - TAILQ_REMOVE(&ramqueue.q_removed, evp, entry); - types[i] = SCHED_REMOVE; - evpids[i] = evp->evpid; - rq_envelope_delete(&ramqueue, evp); - - if (++i == *count) - break; - } - - if (mask & SCHED_EXPIRE && (evp = TAILQ_FIRST(&ramqueue.q_expired))) { - TAILQ_REMOVE(&ramqueue.q_expired, evp, entry); - types[i] = SCHED_EXPIRE; - evpids[i] = evp->evpid; - rq_envelope_delete(&ramqueue, evp); - - if (++i == *count) - break; - } - - if (mask & SCHED_UPDATE && (evp = TAILQ_FIRST(&ramqueue.q_update))) { - TAILQ_REMOVE(&ramqueue.q_update, evp, entry); - types[i] = SCHED_UPDATE; - evpids[i] = evp->evpid; - - if (evp->flags & RQ_ENVELOPE_OVERFLOW) - t = BACKOFF_OVERFLOW; - else if (evp->type == D_MTA) - t = BACKOFF_TRANSFER; - else - t = BACKOFF_DELIVERY; - - evp->sched = scheduler_next(evp->ctime, t, 0); - evp->flags &= ~(RQ_ENVELOPE_UPDATE|RQ_ENVELOPE_OVERFLOW); - evp->state = RQ_EVPSTATE_PENDING; - if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) - sorted_insert(&ramqueue, evp); - - if (++i == *count) - break; - } - - if (mask & SCHED_BOUNCE && (evp = TAILQ_FIRST(&ramqueue.q_bounce))) { - TAILQ_REMOVE(&ramqueue.q_bounce, evp, entry); - types[i] = SCHED_BOUNCE; - evpids[i] = evp->evpid; - - TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry); - evp->state = RQ_EVPSTATE_INFLIGHT; - evp->t_inflight = currtime; - - if (++i == *count) - break; - } - - if (mask & SCHED_MDA && (evp = TAILQ_FIRST(&ramqueue.q_mda))) { - TAILQ_REMOVE(&ramqueue.q_mda, evp, entry); - types[i] = SCHED_MDA; - evpids[i] = evp->evpid; - - TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry); - evp->state = RQ_EVPSTATE_INFLIGHT; - evp->t_inflight = currtime; - - if (++i == *count) - break; - } - - if (mask & SCHED_MTA && (evp = TAILQ_FIRST(&ramqueue.q_mta))) { - TAILQ_REMOVE(&ramqueue.q_mta, evp, entry); - types[i] = SCHED_MTA; - evpids[i] = evp->evpid; - - TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry); - evp->state = RQ_EVPSTATE_INFLIGHT; - evp->t_inflight = currtime; - - if (++i == *count) - break; - } - - /* nothing seen this round */ - if (i == n) - break; - - n = i; - } - - if (i) { - *count = i; - return (1); - } - - if ((evp = TAILQ_FIRST(&ramqueue.q_pending))) { - if (evp->sched < evp->expire) - t = evp->sched; - else - t = evp->expire; - *delay = (t < currtime) ? 0 : (t - currtime); - } - else - *delay = -1; - - return (0); -} - -static size_t -scheduler_ram_messages(uint32_t from, uint32_t *dst, size_t size) -{ - uint64_t id; - size_t n; - void *i; - - for (n = 0, i = NULL; n < size; n++) { - if (tree_iterfrom(&ramqueue.messages, &i, from, &id, NULL) == 0) - break; - dst[n] = id; - } - - return (n); -} - -static size_t -scheduler_ram_envelopes(uint64_t from, struct evpstate *dst, size_t size) -{ - struct rq_message *msg; - struct rq_envelope *evp; - void *i; - size_t n; - - if ((msg = tree_get(&ramqueue.messages, evpid_to_msgid(from))) == NULL) - return (0); - - for (n = 0, i = NULL; n < size; ) { - - if (tree_iterfrom(&msg->envelopes, &i, from, NULL, - (void**)&evp) == 0) - break; - - if (evp->flags & (RQ_ENVELOPE_REMOVED | RQ_ENVELOPE_EXPIRED)) - continue; - - dst[n].evpid = evp->evpid; - dst[n].flags = 0; - dst[n].retry = 0; - dst[n].time = 0; - - if (evp->state == RQ_EVPSTATE_PENDING) { - dst[n].time = evp->sched; - dst[n].flags = EF_PENDING; - } - else if (evp->state == RQ_EVPSTATE_SCHEDULED) { - dst[n].time = evp->t_scheduled; - dst[n].flags = EF_PENDING; - } - else if (evp->state == RQ_EVPSTATE_INFLIGHT) { - dst[n].time = evp->t_inflight; - dst[n].flags = EF_INFLIGHT; - } - else if (evp->state == RQ_EVPSTATE_HELD) { - /* same as scheduled */ - dst[n].time = evp->t_scheduled; - dst[n].flags = EF_PENDING; - dst[n].flags |= EF_HOLD; - } - if (evp->flags & RQ_ENVELOPE_SUSPEND) - dst[n].flags |= EF_SUSPEND; - - n++; - } - - return (n); -} - -static int -scheduler_ram_schedule(uint64_t evpid) -{ - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - void *i; - int r; - - currtime = time(NULL); - - if (evpid > 0xffffffff) { - msgid = evpid_to_msgid(evpid); - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) - return (0); - if (evp->state == RQ_EVPSTATE_INFLIGHT) - return (0); - rq_envelope_schedule(&ramqueue, evp); - return (1); - } - else { - msgid = evpid; - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - i = NULL; - r = 0; - while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) { - if (evp->state == RQ_EVPSTATE_INFLIGHT) - continue; - rq_envelope_schedule(&ramqueue, evp); - r++; - } - return (r); - } -} - -static int -scheduler_ram_remove(uint64_t evpid) -{ - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - void *i; - int r; - - currtime = time(NULL); - - if (evpid > 0xffffffff) { - msgid = evpid_to_msgid(evpid); - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) - return (0); - if (rq_envelope_remove(&ramqueue, evp)) - return (1); - return (0); - } - else { - msgid = evpid; - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - i = NULL; - r = 0; - while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) - if (rq_envelope_remove(&ramqueue, evp)) - r++; - return (r); - } -} - -static int -scheduler_ram_suspend(uint64_t evpid) -{ - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - void *i; - int r; - - currtime = time(NULL); - - if (evpid > 0xffffffff) { - msgid = evpid_to_msgid(evpid); - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) - return (0); - if (rq_envelope_suspend(&ramqueue, evp)) - return (1); - return (0); - } - else { - msgid = evpid; - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - i = NULL; - r = 0; - while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) - if (rq_envelope_suspend(&ramqueue, evp)) - r++; - return (r); - } -} - -static int -scheduler_ram_resume(uint64_t evpid) -{ - struct rq_message *msg; - struct rq_envelope *evp; - uint32_t msgid; - void *i; - int r; - - currtime = time(NULL); - - if (evpid > 0xffffffff) { - msgid = evpid_to_msgid(evpid); - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) - return (0); - if (rq_envelope_resume(&ramqueue, evp)) - return (1); - return (0); - } - else { - msgid = evpid; - if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) - return (0); - i = NULL; - r = 0; - while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) - if (rq_envelope_resume(&ramqueue, evp)) - r++; - return (r); - } -} - -static int -scheduler_ram_query(uint64_t evpid) -{ - uint32_t msgid; - - if (evpid > 0xffffffff) - msgid = evpid_to_msgid(evpid); - else - msgid = evpid; - - if (tree_get(&ramqueue.messages, msgid) == NULL) - return (0); - - return (1); -} - -static void -sorted_insert(struct rq_queue *rq, struct rq_envelope *evp) -{ - struct rq_envelope *evp2; - - SPLAY_INSERT(prioqtree, &rq->q_priotree, evp); - evp2 = SPLAY_NEXT(prioqtree, &rq->q_priotree, evp); - if (evp2) - TAILQ_INSERT_BEFORE(evp2, evp, entry); - else - TAILQ_INSERT_TAIL(&rq->q_pending, evp, entry); -} - -static void -rq_queue_init(struct rq_queue *rq) -{ - memset(rq, 0, sizeof *rq); - tree_init(&rq->messages); - TAILQ_INIT(&rq->q_pending); - TAILQ_INIT(&rq->q_inflight); - TAILQ_INIT(&rq->q_mta); - TAILQ_INIT(&rq->q_mda); - TAILQ_INIT(&rq->q_bounce); - TAILQ_INIT(&rq->q_update); - TAILQ_INIT(&rq->q_expired); - TAILQ_INIT(&rq->q_removed); - SPLAY_INIT(&rq->q_priotree); -} - -static void -rq_queue_merge(struct rq_queue *rq, struct rq_queue *update) -{ - struct rq_message *message, *tomessage; - struct rq_envelope *envelope; - uint64_t id; - void *i; - - while (tree_poproot(&update->messages, &id, (void*)&message)) { - if ((tomessage = tree_get(&rq->messages, id)) == NULL) { - /* message does not exist. re-use structure */ - tree_xset(&rq->messages, id, message); - continue; - } - /* need to re-link all envelopes before merging them */ - i = NULL; - while ((tree_iter(&message->envelopes, &i, &id, - (void*)&envelope))) - envelope->message = tomessage; - tree_merge(&tomessage->envelopes, &message->envelopes); - free(message); - stat_decrement("scheduler.ramqueue.message", 1); - } - - /* Sorted insert in the pending queue */ - while ((envelope = TAILQ_FIRST(&update->q_pending))) { - TAILQ_REMOVE(&update->q_pending, envelope, entry); - sorted_insert(rq, envelope); - } - - rq->evpcount += update->evpcount; -} - -#define SCHEDULEMAX 1024 - -static void -rq_queue_schedule(struct rq_queue *rq) -{ - struct rq_envelope *evp; - size_t n; - - n = 0; - while ((evp = TAILQ_FIRST(&rq->q_pending))) { - if (evp->sched > currtime && evp->expire > currtime) - break; - - if (n == SCHEDULEMAX) - break; - - if (evp->state != RQ_EVPSTATE_PENDING) - errx(1, "evp:%016" PRIx64 " flags=0x%x", evp->evpid, - evp->flags); - - if (evp->expire <= currtime) { - TAILQ_REMOVE(&rq->q_pending, evp, entry); - SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); - TAILQ_INSERT_TAIL(&rq->q_expired, evp, entry); - evp->state = RQ_EVPSTATE_SCHEDULED; - evp->flags |= RQ_ENVELOPE_EXPIRED; - evp->t_scheduled = currtime; - continue; - } - rq_envelope_schedule(rq, evp); - n += 1; - } -} - -static struct evplist * -rq_envelope_list(struct rq_queue *rq, struct rq_envelope *evp) -{ - switch (evp->state) { - case RQ_EVPSTATE_PENDING: - return &rq->q_pending; - - case RQ_EVPSTATE_SCHEDULED: - if (evp->flags & RQ_ENVELOPE_EXPIRED) - return &rq->q_expired; - if (evp->flags & RQ_ENVELOPE_REMOVED) - return &rq->q_removed; - if (evp->flags & RQ_ENVELOPE_UPDATE) - return &rq->q_update; - if (evp->type == D_MTA) - return &rq->q_mta; - if (evp->type == D_MDA) - return &rq->q_mda; - if (evp->type == D_BOUNCE) - return &rq->q_bounce; - errx(1, "%016" PRIx64 " bad evp type %d", evp->evpid, evp->type); - - case RQ_EVPSTATE_INFLIGHT: - return &rq->q_inflight; - - case RQ_EVPSTATE_HELD: - return (NULL); - } - - errx(1, "%016" PRIx64 " bad state %d", evp->evpid, evp->state); - return (NULL); -} - -static void -rq_envelope_schedule(struct rq_queue *rq, struct rq_envelope *evp) -{ - struct rq_holdq *hq; - struct evplist *q = NULL; - - switch (evp->type) { - case D_MTA: - q = &rq->q_mta; - break; - case D_MDA: - q = &rq->q_mda; - break; - case D_BOUNCE: - q = &rq->q_bounce; - break; - } - - if (evp->flags & RQ_ENVELOPE_UPDATE) - q = &rq->q_update; - - if (evp->state == RQ_EVPSTATE_HELD) { - hq = tree_xget(&holdqs[evp->type], evp->holdq); - TAILQ_REMOVE(&hq->q, evp, entry); - hq->count -= 1; - if (TAILQ_EMPTY(&hq->q)) { - tree_xpop(&holdqs[evp->type], evp->holdq); - free(hq); - } - evp->holdq = 0; - stat_decrement("scheduler.ramqueue.hold", 1); - } - else if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) { - TAILQ_REMOVE(&rq->q_pending, evp, entry); - SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); - } - - TAILQ_INSERT_TAIL(q, evp, entry); - evp->state = RQ_EVPSTATE_SCHEDULED; - evp->t_scheduled = currtime; -} - -static int -rq_envelope_remove(struct rq_queue *rq, struct rq_envelope *evp) -{ - struct rq_holdq *hq; - struct evplist *evl; - - if (evp->flags & (RQ_ENVELOPE_REMOVED | RQ_ENVELOPE_EXPIRED)) - return (0); - /* - * If envelope is inflight, mark it envelope for removal. - */ - if (evp->state == RQ_EVPSTATE_INFLIGHT) { - evp->flags |= RQ_ENVELOPE_REMOVED; - return (1); - } - - if (evp->state == RQ_EVPSTATE_HELD) { - hq = tree_xget(&holdqs[evp->type], evp->holdq); - TAILQ_REMOVE(&hq->q, evp, entry); - hq->count -= 1; - if (TAILQ_EMPTY(&hq->q)) { - tree_xpop(&holdqs[evp->type], evp->holdq); - free(hq); - } - evp->holdq = 0; - stat_decrement("scheduler.ramqueue.hold", 1); - } - else if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) { - evl = rq_envelope_list(rq, evp); - TAILQ_REMOVE(evl, evp, entry); - if (evl == &rq->q_pending) - SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); - } - - TAILQ_INSERT_TAIL(&rq->q_removed, evp, entry); - evp->state = RQ_EVPSTATE_SCHEDULED; - evp->flags |= RQ_ENVELOPE_REMOVED; - evp->t_scheduled = currtime; - - return (1); -} - -static int -rq_envelope_suspend(struct rq_queue *rq, struct rq_envelope *evp) -{ - struct rq_holdq *hq; - struct evplist *evl; - - if (evp->flags & RQ_ENVELOPE_SUSPEND) - return (0); - - if (evp->state == RQ_EVPSTATE_HELD) { - hq = tree_xget(&holdqs[evp->type], evp->holdq); - TAILQ_REMOVE(&hq->q, evp, entry); - hq->count -= 1; - if (TAILQ_EMPTY(&hq->q)) { - tree_xpop(&holdqs[evp->type], evp->holdq); - free(hq); - } - evp->holdq = 0; - evp->state = RQ_EVPSTATE_PENDING; - stat_decrement("scheduler.ramqueue.hold", 1); - } - else if (evp->state != RQ_EVPSTATE_INFLIGHT) { - evl = rq_envelope_list(rq, evp); - TAILQ_REMOVE(evl, evp, entry); - if (evl == &rq->q_pending) - SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); - } - - evp->flags |= RQ_ENVELOPE_SUSPEND; - - return (1); -} - -static int -rq_envelope_resume(struct rq_queue *rq, struct rq_envelope *evp) -{ - struct evplist *evl; - - if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) - return (0); - - if (evp->state != RQ_EVPSTATE_INFLIGHT) { - evl = rq_envelope_list(rq, evp); - if (evl == &rq->q_pending) - sorted_insert(rq, evp); - else - TAILQ_INSERT_TAIL(evl, evp, entry); - } - - evp->flags &= ~RQ_ENVELOPE_SUSPEND; - - return (1); -} - -static void -rq_envelope_delete(struct rq_queue *rq, struct rq_envelope *evp) -{ - tree_xpop(&evp->message->envelopes, evp->evpid); - if (tree_empty(&evp->message->envelopes)) { - tree_xpop(&rq->messages, evp->message->msgid); - free(evp->message); - stat_decrement("scheduler.ramqueue.message", 1); - } - - free(evp); - rq->evpcount--; - stat_decrement("scheduler.ramqueue.envelope", 1); -} - -static const char * -rq_envelope_to_text(struct rq_envelope *e) -{ - static char buf[256]; - char t[64]; - - (void)snprintf(buf, sizeof buf, "evp:%016" PRIx64 " [", e->evpid); - - if (e->type == D_BOUNCE) - (void)strlcat(buf, "bounce", sizeof buf); - else if (e->type == D_MDA) - (void)strlcat(buf, "mda", sizeof buf); - else if (e->type == D_MTA) - (void)strlcat(buf, "mta", sizeof buf); - - (void)snprintf(t, sizeof t, ",expire=%s", - duration_to_text(e->expire - currtime)); - (void)strlcat(buf, t, sizeof buf); - - - switch (e->state) { - case RQ_EVPSTATE_PENDING: - (void)snprintf(t, sizeof t, ",pending=%s", - duration_to_text(e->sched - currtime)); - (void)strlcat(buf, t, sizeof buf); - break; - - case RQ_EVPSTATE_SCHEDULED: - (void)snprintf(t, sizeof t, ",scheduled=%s", - duration_to_text(currtime - e->t_scheduled)); - (void)strlcat(buf, t, sizeof buf); - break; - - case RQ_EVPSTATE_INFLIGHT: - (void)snprintf(t, sizeof t, ",inflight=%s", - duration_to_text(currtime - e->t_inflight)); - (void)strlcat(buf, t, sizeof buf); - break; - - case RQ_EVPSTATE_HELD: - (void)snprintf(t, sizeof t, ",held=%s", - duration_to_text(currtime - e->t_inflight)); - (void)strlcat(buf, t, sizeof buf); - break; - default: - errx(1, "%016" PRIx64 " bad state %d", e->evpid, e->state); - } - - if (e->flags & RQ_ENVELOPE_REMOVED) - (void)strlcat(buf, ",removed", sizeof buf); - if (e->flags & RQ_ENVELOPE_EXPIRED) - (void)strlcat(buf, ",expired", sizeof buf); - if (e->flags & RQ_ENVELOPE_SUSPEND) - (void)strlcat(buf, ",suspended", sizeof buf); - - (void)strlcat(buf, "]", sizeof buf); - - return (buf); -} - -static void -rq_queue_dump(struct rq_queue *rq, const char * name) -{ - struct rq_message *message; - struct rq_envelope *envelope; - void *i, *j; - uint64_t id; - - log_debug("debug: /--- ramqueue: %s", name); - - i = NULL; - while ((tree_iter(&rq->messages, &i, &id, (void*)&message))) { - log_debug("debug: | msg:%08" PRIx32, message->msgid); - j = NULL; - while ((tree_iter(&message->envelopes, &j, &id, - (void*)&envelope))) - log_debug("debug: | %s", - rq_envelope_to_text(envelope)); - } - log_debug("debug: \\---"); -} - -static int -rq_envelope_cmp(struct rq_envelope *e1, struct rq_envelope *e2) -{ - time_t ref1, ref2; - - ref1 = (e1->sched < e1->expire) ? e1->sched : e1->expire; - ref2 = (e2->sched < e2->expire) ? e2->sched : e2->expire; - if (ref1 != ref2) - return (ref1 < ref2) ? -1 : 1; - - if (e1->evpid != e2->evpid) - return (e1->evpid < e2->evpid) ? -1 : 1; - - return 0; -} - -SPLAY_GENERATE(prioqtree, rq_envelope, t_entry, rq_envelope_cmp); diff --git a/smtpd/sendmail.8 b/smtpd/sendmail.8 deleted file mode 100644 index 1696a861..00000000 --- a/smtpd/sendmail.8 +++ /dev/null @@ -1,86 +0,0 @@ -.\" $OpenBSD: sendmail.8,v 1.4 2015/10/23 15:48:16 jung Exp $ -.\" -.\" Copyright (C) 2013 Ryan Kavanagh <rak@debian.org> -.\" All rights reserved. -.\" -.\" Permission to use, copy, modify, and/or distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.Dd $Mdocdate: October 23 2015 $ -.Dt SENDMAIL 8 -.Os -.Sh NAME -.Nm sendmail -.Nd a mail enqueuer for -.Xr smtpd 8 -.Sh SYNOPSIS -.Nm -.Op Fl tv -.Op Fl F Ar name -.Op Fl f Ar from -.Ar to ... -.Sh DESCRIPTION -The -.Nm -utility is a local enqueuer for the -.Xr smtpd 8 -daemon, -compatible with -.Xr mailwrapper 8 . -The message is read on standard input (stdin) until -.Nm -encounters an end-of-file. -The -.Nm -enqueuer is not intended to be used directly to send mail, -but rather via a frontend known as a mail user agent. -.Pp -Unless the optional -.Fl t -flag is specified, -one or more recipients must be specified on the command line. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl F Ar name -Set the sender's full name. -.It Fl f Ar from -Set the sender's address. -.It Fl t -Read the message's To:, Cc:, and Bcc: fields for recipients. -The Bcc: field will be deleted before sending. -.It Fl v -Enable verbose output. -.El -.Pp -To maintain compatibility with Sendmail, Inc.'s implementation of -.Nm , -various other flags are accepted, -but have no effect. -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr smtpctl 8 , -.Xr smtpd 8 -.Sh AUTHORS -.Sy OpenSMTPD -is primarily developed by Gilles Chehade, -Eric Faurot, -and Charles Longeau, -with contributions from various -.Ox -hackers. -It is distributed under the ISC license. -.Pp -This manpage was written by -.An Ryan Kavanagh -.Aq Mt rak@debian.org -for the Debian project and is distributed under the ISC license. diff --git a/smtpd/smtp.1 b/smtpd/smtp.1 deleted file mode 100644 index 3cc03844..00000000 --- a/smtpd/smtp.1 +++ /dev/null @@ -1,96 +0,0 @@ -.\" $OpenBSD: smtp.1,v 1.7 2018/07/04 08:23:43 jmc Exp $ -.\" -.\" Copyright (c) 2018, Eric Faurot <eric@openbsd.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: July 4 2018 $ -.Dt SMTP 1 -.Os -.Sh NAME -.Nm smtp -.Nd Simple Mail Transfer Protocol client -.Sh SYNOPSIS -.Nm -.Op Fl Chnv -.Op Fl F Ar from -.Op Fl H Ar helo -.Op Fl s Ar server -.Op Ar recipient ... -.Sh DESCRIPTION -The -.Nm -utility is a Simple Mail Transfer Protocol -.Pq SMTP -client which can be used to run an SMTP transaction against an SMTP server. -.Pp -By default, -.Nm -reads the mail content from the standard input, establishes an SMTP session, -and runs an SMTP transaction for all the specified recipients. -The content is sent unaltered as mail data. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl C -Do not require server certificate to be valid. -.It Fl F Ar from -Set the return-path (MAIL FROM) for the SMTP transaction. -Default to the current username. -.It Fl H Ar helo -Define the hostname to advertise (HELO) when establishing the SMTP session. -.It Fl h -Display version and usage. -.It Fl n -Do not actually execute a transaction, -just try to establish an SMTP session and quit. -When this option is given, no message is read from the standard input. -.It Fl s Ar server -Specify the server to connect to and connection parameters. -The format is -.Sm off -.Op Ar proto No :// Op Ar user : pass No @ -.Ar host Op : Ar port . -.Sm on -The following protocols are available: -.Pp -.Bl -tag -width "smtp+notls" -compact -.It smtp -Normal SMTP session with opportunistic STARTTLS. -.It smtp+tls -Normal SMTP session with mandatory STARTTLS. -.It smtp+notls -Plain text SMTP session without TLS. -.It lmtp -LMTP session with opportunistic STARTTLS. -.It lmtp+tls -LMTP session with mandatory STARTTLS. -.It lmtp+notls -Plain text LMTP session without TLS. -.It smtps -SMTP session with forced TLS on connection. -.El -.Pp -Defaults to -.Dq smtp://localhost:25 . -.It Fl v -Be more verbose. -This option can be specified multiple times. -.El -.Sh SEE ALSO -.Xr smtpd 8 -.Sh HISTORY -The -.Nm -program first appeared in -.Ox 6.4 . diff --git a/smtpd/smtp.c b/smtpd/smtp.c deleted file mode 100644 index 602fd0d6..00000000 --- a/smtpd/smtp.c +++ /dev/null @@ -1,387 +0,0 @@ -/* $OpenBSD: smtp.c,v 1.166 2019/08/10 16:07:01 gilles Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <grp.h> /* needed for setgroups */ -#include <imsg.h> -#include <netdb.h> -#include <pwd.h> -#include <signal.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <openssl/ssl.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -static void smtp_setup_events(void); -static void smtp_pause(void); -static void smtp_resume(void); -static void smtp_accept(int, short, void *); -static void smtp_dropped(struct listener *, int, const struct sockaddr_storage *); -static int smtp_enqueue(void); -static int smtp_can_accept(void); -static void smtp_setup_listeners(void); -static int smtp_sni_callback(SSL *, int *, void *); - -int -proxy_session(struct listener *listener, int sock, - const struct sockaddr_storage *ss, - void (*accepted)(struct listener *, int, - const struct sockaddr_storage *, struct io *), - void (*dropped)(struct listener *, int, - const struct sockaddr_storage *)); - -static void smtp_accepted(struct listener *, int, const struct sockaddr_storage *, struct io *); - - -#define SMTP_FD_RESERVE 5 -#define getdtablecount() 0 - -static size_t sessions; -static size_t maxsessions; - -void -smtp_imsg(struct mproc *p, struct imsg *imsg) -{ - switch (imsg->hdr.type) { - case IMSG_SMTP_CHECK_SENDER: - case IMSG_SMTP_EXPAND_RCPT: - case IMSG_SMTP_LOOKUP_HELO: - case IMSG_SMTP_AUTHENTICATE: - case IMSG_FILTER_SMTP_PROTOCOL: - case IMSG_FILTER_SMTP_DATA_BEGIN: - smtp_session_imsg(p, imsg); - return; - - case IMSG_SMTP_MESSAGE_COMMIT: - case IMSG_SMTP_MESSAGE_CREATE: - case IMSG_SMTP_MESSAGE_OPEN: - case IMSG_QUEUE_ENVELOPE_SUBMIT: - case IMSG_QUEUE_ENVELOPE_COMMIT: - smtp_session_imsg(p, imsg); - return; - - case IMSG_QUEUE_SMTP_SESSION: - m_compose(p, IMSG_QUEUE_SMTP_SESSION, 0, 0, smtp_enqueue(), - imsg->data, imsg->hdr.len - sizeof imsg->hdr); - return; - - case IMSG_CTL_SMTP_SESSION: - m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0, - smtp_enqueue(), NULL, 0); - return; - - case IMSG_CTL_PAUSE_SMTP: - log_debug("debug: smtp: pausing listening sockets"); - smtp_pause(); - env->sc_flags |= SMTPD_SMTP_PAUSED; - return; - - case IMSG_CTL_RESUME_SMTP: - log_debug("debug: smtp: resuming listening sockets"); - env->sc_flags &= ~SMTPD_SMTP_PAUSED; - smtp_resume(); - return; - } - - errx(1, "smtp_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); -} - -void -smtp_postfork(void) -{ - smtp_setup_listeners(); -} - -void -smtp_postprivdrop(void) -{ -} - -void -smtp_configure(void) -{ - smtp_setup_events(); -} - -static void -smtp_setup_listeners(void) -{ - struct listener *l; - int opt; - - TAILQ_FOREACH(l, env->sc_listeners, entry) { - if ((l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0)) == -1) { - if (errno == EAFNOSUPPORT) { - log_warn("smtpd: socket"); - continue; - } - fatal("smtpd: socket"); - } - opt = 1; -#ifdef SO_REUSEADDR - if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, - sizeof(opt)) == -1) - fatal("smtpd: setsockopt"); -#else - if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEPORT, &opt, - sizeof(opt)) < 0) - fatal("smtpd: setsockopt"); -#endif -#ifdef IPV6_V6ONLY - /* - * If using IPv6, bind only to IPv6 if possible. - * This avoids ambiguities with IPv4-mapped IPv6 addresses. - */ - if (l->ss.ss_family == AF_INET6) - if (setsockopt(l->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, - sizeof(opt)) < 0) - fatal("smtpd: setsockopt"); -#endif - if (bind(l->fd, (struct sockaddr *)&l->ss, SS_LEN(&l->ss)) == -1) - fatal("smtpd: bind"); - } -} - -static void -smtp_setup_events(void) -{ - struct listener *l; - struct pki *pki; - SSL_CTX *ssl_ctx; - void *iter; - const char *k; - - TAILQ_FOREACH(l, env->sc_listeners, entry) { - log_debug("debug: smtp: listen on %s port %d flags 0x%01x" - " pki \"%s\"" - " ca \"%s\"", ss_to_text(&l->ss), ntohs(l->port), - l->flags, l->pki_name, l->ca_name); - - io_set_nonblocking(l->fd); - if (listen(l->fd, SMTPD_BACKLOG) == -1) - fatal("listen"); - event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l); - - if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) - event_add(&l->ev, NULL); - } - - iter = NULL; - while (dict_iter(env->sc_pki_dict, &iter, &k, (void **)&pki)) { - if (!ssl_setup((SSL_CTX **)&ssl_ctx, pki, smtp_sni_callback, - env->sc_tls_ciphers)) - fatal("smtp_setup_events: ssl_setup failure"); - dict_xset(env->sc_ssl_dict, k, ssl_ctx); - } - - purge_config(PURGE_PKI_KEYS); - - maxsessions = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE; - log_debug("debug: smtp: will accept at most %zu clients", maxsessions); -} - -static void -smtp_pause(void) -{ - struct listener *l; - - if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) - return; - - TAILQ_FOREACH(l, env->sc_listeners, entry) - event_del(&l->ev); -} - -static void -smtp_resume(void) -{ - struct listener *l; - - if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED)) - return; - - TAILQ_FOREACH(l, env->sc_listeners, entry) - event_add(&l->ev, NULL); -} - -static int -smtp_enqueue(void) -{ - struct listener *listener = env->sc_sock_listener; - int fd[2]; - - /* - * Some enqueue requests buffered in IMSG may still arrive even after - * call to smtp_pause() because enqueue listener is not a real socket - * and thus cannot be paused properly. - */ - if (env->sc_flags & SMTPD_SMTP_PAUSED) - return (-1); - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd)) - return (-1); - - if ((smtp_session(listener, fd[0], &listener->ss, env->sc_hostname, NULL)) == -1) { - close(fd[0]); - close(fd[1]); - return (-1); - } - - sessions++; - stat_increment("smtp.session", 1); - stat_increment("smtp.session.local", 1); - - return (fd[1]); -} - -static void -smtp_accept(int fd, short event, void *p) -{ - struct listener *listener = p; - struct sockaddr_storage ss; - socklen_t len; - int sock; - - if (env->sc_flags & SMTPD_SMTP_PAUSED) - fatalx("smtp_session: unexpected client"); - - if (!smtp_can_accept()) { - log_warnx("warn: Disabling incoming SMTP connections: " - "Client limit reached"); - goto pause; - } - - len = sizeof(ss); - if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) { - if (errno == ENFILE || errno == EMFILE) { - log_warn("warn: Disabling incoming SMTP connections"); - goto pause; - } - if (errno == EINTR || errno == EWOULDBLOCK || - errno == ECONNABORTED) - return; - fatal("smtp_accept"); - } - - if (listener->flags & F_PROXY) { - io_set_nonblocking(sock); - if (proxy_session(listener, sock, &ss, - smtp_accepted, smtp_dropped) == -1) { - close(sock); - return; - } - return; - } - - smtp_accepted(listener, sock, &ss, NULL); - return; - -pause: - smtp_pause(); - env->sc_flags |= SMTPD_SMTP_DISABLED; - return; -} - -static int -smtp_can_accept(void) -{ - if (sessions + 1 == maxsessions) - return 0; - return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2); -} - -void -smtp_collect(void) -{ - sessions--; - stat_decrement("smtp.session", 1); - - if (!smtp_can_accept()) - return; - - if (env->sc_flags & SMTPD_SMTP_DISABLED) { - log_warnx("warn: smtp: " - "fd exhaustion over, re-enabling incoming connections"); - env->sc_flags &= ~SMTPD_SMTP_DISABLED; - smtp_resume(); - } -} - -static int -smtp_sni_callback(SSL *ssl, int *ad, void *arg) -{ - const char *sn; - void *ssl_ctx; - - sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (sn == NULL) - return SSL_TLSEXT_ERR_NOACK; - ssl_ctx = dict_get(env->sc_ssl_dict, sn); - if (ssl_ctx == NULL) - return SSL_TLSEXT_ERR_NOACK; - SSL_set_SSL_CTX(ssl, ssl_ctx); - return SSL_TLSEXT_ERR_OK; -} - -static void -smtp_accepted(struct listener *listener, int sock, const struct sockaddr_storage *ss, struct io *io) -{ - int ret; - - ret = smtp_session(listener, sock, ss, NULL, io); - if (ret == -1) { - log_warn("warn: Failed to create SMTP session"); - close(sock); - return; - } - io_set_nonblocking(sock); - - sessions++; - stat_increment("smtp.session", 1); - if (listener->ss.ss_family == AF_LOCAL) - stat_increment("smtp.session.local", 1); - if (listener->ss.ss_family == AF_INET) - stat_increment("smtp.session.inet4", 1); - if (listener->ss.ss_family == AF_INET6) - stat_increment("smtp.session.inet6", 1); -} - -static void -smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss) -{ - close(sock); - sessions--; -} diff --git a/smtpd/smtp.h b/smtpd/smtp.h deleted file mode 100644 index dc91d878..00000000 --- a/smtpd/smtp.h +++ /dev/null @@ -1,95 +0,0 @@ -/* $OpenBSD: smtp.h,v 1.3 2019/09/02 20:05:21 eric Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define TLS_NO 0 -#define TLS_YES 1 -#define TLS_FORCE 2 -#define TLS_SMTPS 3 - -#define CERT_OK 0 -#define CERT_UNKNOWN 1 -#define CERT_INVALID 2 - -#define FAIL_INTERNAL 1 /* malloc, etc. (check errno) */ -#define FAIL_CONN 2 /* disconnect, timeout... (check errno) */ -#define FAIL_PROTO 3 /* protocol violation */ -#define FAIL_IMPL 4 /* server lacks a required feature */ -#define FAIL_RESP 5 /* rejected command */ - -struct smtp_params { - - /* Client options */ - size_t linemax; /* max input line size */ - size_t ibufmax; /* max input buffer size */ - size_t obufmax; /* max output buffer size */ - - /* Connection settings */ - const struct sockaddr *dst; /* address to connect to */ - const struct sockaddr *src; /* address to bind to */ - int timeout; /* timeout in seconds */ - - /* TLS options */ - int tls_req; /* requested TLS mode */ - int tls_verify; /* need valid server certificate */ - - /* SMTP options */ - int lmtp; /* use LMTP protocol */ - const char *helo; /* string to use with HELO */ - const char *auth_user; /* for AUTH */ - const char *auth_pass; /* for AUTH */ -}; - -struct smtp_rcpt { - const char *to; - const char *dsn_notify; - const char *dsn_orcpt; - int done; -}; - -struct smtp_mail { - const char *from; - const char *dsn_ret; - const char *dsn_envid; - struct smtp_rcpt *rcpt; - int rcptcount; - FILE *fp; -}; - -struct smtp_status { - struct smtp_rcpt *rcpt; - const char *cmd; - const char *status; -}; - -struct smtp_client; - -/* smtp_client.c */ -struct smtp_client *smtp_connect(const struct smtp_params *, void *); -void smtp_cert_verified(struct smtp_client *, int); -void smtp_set_tls(struct smtp_client *, void *); -void smtp_quit(struct smtp_client *); -void smtp_sendmail(struct smtp_client *, struct smtp_mail *); - -/* callbacks */ -void smtp_verify_server_cert(void *, struct smtp_client *, void *); -void smtp_require_tls(void *, struct smtp_client *); -void smtp_ready(void *, struct smtp_client *); -void smtp_failed(void *, struct smtp_client *, int, const char *); -void smtp_closed(void *, struct smtp_client *); -void smtp_status(void *, struct smtp_client *, struct smtp_status *); -void smtp_done(void *, struct smtp_client *, struct smtp_mail *); diff --git a/smtpd/smtp/Makefile b/smtpd/smtp/Makefile deleted file mode 100644 index 380e3ad6..00000000 --- a/smtpd/smtp/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $OpenBSD: Makefile,v 1.1 2018/04/26 13:57:13 eric Exp $ - -.PATH: ${.CURDIR}/.. - -PROG= smtp - -BINDIR= /usr/bin -MAN= smtp.1 - -SRCS+= iobuf.c -SRCS+= ioev.c -SRCS+= log.c -SRCS+= smtp_client.c -SRCS+= smtpc.c -SRCS+= ssl.c -SRCS+= ssl_smtpd.c -SRCS+= ssl_verify.c - -CPPFLAGS+= -DIO_TLS - -LDADD+= -levent -lutil -lssl -lcrypto -lm -lz -DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ} - -.include <bsd.prog.mk> diff --git a/smtpd/smtp_client.c b/smtpd/smtp_client.c deleted file mode 100644 index 8e146e1b..00000000 --- a/smtpd/smtp_client.c +++ /dev/null @@ -1,923 +0,0 @@ -/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <resolv.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "log.h" -#include "ioev.h" -#include "smtp.h" - -#define TRACE_SMTPCLT 2 -#define TRACE_IO 3 - -enum { - STATE_INIT = 0, - STATE_BANNER, - STATE_EHLO, - STATE_HELO, - STATE_LHLO, - STATE_STARTTLS, - STATE_AUTH, - STATE_AUTH_PLAIN, - STATE_AUTH_LOGIN, - STATE_AUTH_LOGIN_USER, - STATE_AUTH_LOGIN_PASS, - STATE_READY, - STATE_MAIL, - STATE_RCPT, - STATE_DATA, - STATE_BODY, - STATE_EOM, - STATE_RSET, - STATE_QUIT, - - STATE_LAST -}; - -#define base64_encode __b64_ntop -#define base64_decode __b64_pton - -#define FLAG_TLS 0x01 -#define FLAG_TLS_VERIFIED 0x02 - -#define SMTP_EXT_STARTTLS 0x01 -#define SMTP_EXT_PIPELINING 0x02 -#define SMTP_EXT_AUTH 0x04 -#define SMTP_EXT_AUTH_PLAIN 0x08 -#define SMTP_EXT_AUTH_LOGIN 0x10 -#define SMTP_EXT_DSN 0x20 -#define SMTP_EXT_SIZE 0x40 - -struct smtp_client { - void *tag; - struct smtp_params params; - - int state; - int flags; - int ext; - size_t ext_size; - - struct io *io; - char *reply; - size_t replysz; - - struct smtp_mail *mail; - int rcptidx; - int rcptok; -}; - -void log_trace_verbose(int); -void log_trace(int, const char *, ...) - __attribute__((format (printf, 2, 3))); - -static void smtp_client_io(struct io *, int, void *); -static void smtp_client_free(struct smtp_client *); -static void smtp_client_state(struct smtp_client *, int); -static void smtp_client_abort(struct smtp_client *, int, const char *); -static void smtp_client_cancel(struct smtp_client *, int, const char *); -static void smtp_client_sendcmd(struct smtp_client *, char *, ...); -static void smtp_client_sendbody(struct smtp_client *); -static int smtp_client_readline(struct smtp_client *); -static int smtp_client_replycat(struct smtp_client *, const char *); -static void smtp_client_response(struct smtp_client *, const char *); -static void smtp_client_mail_abort(struct smtp_client *); -static void smtp_client_mail_status(struct smtp_client *, const char *); -static void smtp_client_rcpt_status(struct smtp_client *, struct smtp_rcpt *, const char *); - -static const char *strstate[STATE_LAST] = { - "INIT", - "BANNER", - "EHLO", - "HELO", - "LHLO", - "STARTTLS", - "AUTH", - "AUTH_PLAIN", - "AUTH_LOGIN", - "AUTH_LOGIN_USER", - "AUTH_LOGIN_PASS", - "READY", - "MAIL", - "RCPT", - "DATA", - "BODY", - "EOM", - "RSET", - "QUIT", -}; - -struct smtp_client * -smtp_connect(const struct smtp_params *params, void *tag) -{ - struct smtp_client *proto; - - proto = calloc(1, sizeof *proto); - if (proto == NULL) - return NULL; - - memmove(&proto->params, params, sizeof(*params)); - proto->tag = tag; - proto->io = io_new(); - if (proto->io == NULL) { - free(proto); - return NULL; - } - io_set_callback(proto->io, smtp_client_io, proto); - io_set_timeout(proto->io, proto->params.timeout); - - if (io_connect(proto->io, proto->params.dst, proto->params.src) == -1) { - smtp_client_abort(proto, FAIL_CONN, io_error(proto->io)); - return NULL; - } - - return proto; -} - -void -smtp_cert_verified(struct smtp_client *proto, int verified) -{ - if (verified == CERT_OK) - proto->flags |= FLAG_TLS_VERIFIED; - - else if (proto->params.tls_verify) { - errno = EAUTH; - smtp_client_abort(proto, FAIL_CONN, - "Invalid server certificate"); - return; - } - - io_resume(proto->io, IO_IN); - - if (proto->state == STATE_INIT) - smtp_client_state(proto, STATE_BANNER); - else { - /* Clear extensions before re-issueing an EHLO command. */ - proto->ext = 0; - smtp_client_state(proto, STATE_EHLO); - } -} - -void -smtp_set_tls(struct smtp_client *proto, void *ctx) -{ - io_start_tls(proto->io, ctx); -} - -void -smtp_quit(struct smtp_client *proto) -{ - if (proto->state != STATE_READY) - fatalx("connection is not ready"); - - smtp_client_state(proto, STATE_QUIT); -} - -void -smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail) -{ - if (proto->state != STATE_READY) - fatalx("connection is not ready"); - - proto->mail = mail; - smtp_client_state(proto, STATE_MAIL); -} - -static void -smtp_client_free(struct smtp_client *proto) -{ - if (proto->mail) - fatalx("current task should have been deleted already"); - - smtp_closed(proto->tag, proto); - - if (proto->io) - io_free(proto->io); - - free(proto->reply); - free(proto); -} - -/* - * End the session immediatly. - */ -static void -smtp_client_abort(struct smtp_client *proto, int err, const char *reason) -{ - smtp_failed(proto->tag, proto, err, reason); - - if (proto->mail) - smtp_client_mail_abort(proto); - - smtp_client_free(proto); -} - -/* - * Properly close the session. - */ -static void -smtp_client_cancel(struct smtp_client *proto, int err, const char *reason) -{ - if (proto->mail) - fatal("not supposed to have a mail"); - - smtp_failed(proto->tag, proto, err, reason); - - smtp_client_state(proto, STATE_QUIT); -} - -static void -smtp_client_state(struct smtp_client *proto, int newstate) -{ - struct smtp_rcpt *rcpt; - char ibuf[LINE_MAX], obuf[LINE_MAX]; - size_t n; - int oldstate; - - if (proto->reply) - proto->reply[0] = '\0'; - - again: - oldstate = proto->state; - proto->state = newstate; - - log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto, - strstate[oldstate], - strstate[newstate]); - - /* don't try this at home! */ -#define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0) - - switch (proto->state) { - case STATE_BANNER: - io_set_read(proto->io); - break; - - case STATE_EHLO: - smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo); - break; - - case STATE_HELO: - smtp_client_sendcmd(proto, "HELO %s", proto->params.helo); - break; - - case STATE_LHLO: - smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo); - break; - - case STATE_STARTTLS: - if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS) - smtp_client_state(proto, STATE_AUTH); - else if (proto->ext & SMTP_EXT_STARTTLS) - smtp_client_sendcmd(proto, "STARTTLS"); - else if (proto->params.tls_req == TLS_FORCE) - smtp_client_cancel(proto, FAIL_IMPL, - "TLS not supported by remote host"); - else - smtp_client_state(proto, STATE_AUTH); - break; - - case STATE_AUTH: - if (!proto->params.auth_user) - smtp_client_state(proto, STATE_READY); - else if ((proto->flags & FLAG_TLS) == 0) - smtp_client_cancel(proto, FAIL_IMPL, - "Authentication requires TLS"); - else if ((proto->ext & SMTP_EXT_AUTH) == 0) - smtp_client_cancel(proto, FAIL_IMPL, - "AUTH not supported by remote host"); - else if (proto->ext & SMTP_EXT_AUTH_PLAIN) - smtp_client_state(proto, STATE_AUTH_PLAIN); - else if (proto->ext & SMTP_EXT_AUTH_LOGIN) - smtp_client_state(proto, STATE_AUTH_LOGIN); - else - smtp_client_cancel(proto, FAIL_IMPL, - "No supported AUTH method"); - break; - - case STATE_AUTH_PLAIN: - (void)strlcpy(ibuf, "-", sizeof(ibuf)); - (void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf)); - if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf)); - if (n >= sizeof(ibuf)) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - *strchr(ibuf, ':') = '\0'; - ibuf[0] = '\0'; - if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf); - explicit_bzero(ibuf, sizeof ibuf); - explicit_bzero(obuf, sizeof obuf); - break; - - case STATE_AUTH_LOGIN: - smtp_client_sendcmd(proto, "AUTH LOGIN"); - break; - - case STATE_AUTH_LOGIN_USER: - if (base64_encode(proto->params.auth_user, - strlen(proto->params.auth_user), obuf, - sizeof(obuf)) == -1) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - smtp_client_sendcmd(proto, "%s", obuf); - explicit_bzero(obuf, sizeof obuf); - break; - - case STATE_AUTH_LOGIN_PASS: - if (base64_encode(proto->params.auth_pass, - strlen(proto->params.auth_pass), obuf, - sizeof(obuf)) == -1) { - errno = EMSGSIZE; - smtp_client_cancel(proto, FAIL_INTERNAL, - "credentials too large"); - break; - } - smtp_client_sendcmd(proto, "%s", obuf); - explicit_bzero(obuf, sizeof obuf); - break; - - case STATE_READY: - smtp_ready(proto->tag, proto); - break; - - case STATE_MAIL: - if (proto->ext & SMTP_EXT_DSN) - smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s", - proto->mail->from, - proto->mail->dsn_ret ? " RET=" : "", - proto->mail->dsn_ret ? proto->mail->dsn_ret : "", - proto->mail->dsn_envid ? " ENVID=" : "", - proto->mail->dsn_envid ? proto->mail->dsn_envid : ""); - else - smtp_client_sendcmd(proto, "MAIL FROM:<%s>", - proto->mail->from); - break; - - case STATE_RCPT: - if (proto->rcptidx == proto->mail->rcptcount) { - smtp_client_state(proto, STATE_DATA); - break; - } - rcpt = &proto->mail->rcpt[proto->rcptidx]; - if (proto->ext & SMTP_EXT_DSN) - smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s", - rcpt->to, - rcpt->dsn_notify ? " NOTIFY=" : "", - rcpt->dsn_notify ? rcpt->dsn_notify : "", - rcpt->dsn_orcpt ? " ORCPT=" : "", - rcpt->dsn_orcpt ? rcpt->dsn_orcpt : ""); - else - smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to); - break; - - case STATE_DATA: - if (proto->rcptok == 0) { - smtp_client_mail_abort(proto); - smtp_client_state(proto, STATE_RSET); - } - else - smtp_client_sendcmd(proto, "DATA"); - break; - - case STATE_BODY: - fseek(proto->mail->fp, 0, SEEK_SET); - smtp_client_sendbody(proto); - break; - - case STATE_EOM: - smtp_client_sendcmd(proto, "."); - break; - - case STATE_RSET: - smtp_client_sendcmd(proto, "RSET"); - break; - - case STATE_QUIT: - smtp_client_sendcmd(proto, "QUIT"); - break; - - default: - fatalx("%s: bad state %d", __func__, proto->state); - } -#undef smtp_client_state -} - -/* - * Handle a response to an SMTP command - */ -static void -smtp_client_response(struct smtp_client *proto, const char *line) -{ - struct smtp_rcpt *rcpt; - int i, seen; - - switch (proto->state) { - case STATE_BANNER: - if (line[0] != '2') - smtp_client_abort(proto, FAIL_RESP, line); - else if (proto->params.lmtp) - smtp_client_state(proto, STATE_LHLO); - else - smtp_client_state(proto, STATE_EHLO); - break; - - case STATE_EHLO: - if (line[0] != '2') { - /* - * Either rejected or not implemented. If we want to - * use EHLO extensions, report an SMTP error. - * Otherwise, fallback to using HELO. - */ - if ((proto->params.tls_req == TLS_FORCE) || - (proto->params.auth_user)) - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_HELO); - break; - } - smtp_client_state(proto, STATE_STARTTLS); - break; - - case STATE_HELO: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_LHLO: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_STARTTLS: - if (line[0] != '2') { - if ((proto->params.tls_req == TLS_FORCE) || - (proto->params.auth_user)) { - smtp_client_cancel(proto, FAIL_RESP, line); - break; - } - smtp_client_state(proto, STATE_AUTH); - } - else - smtp_require_tls(proto->tag, proto); - break; - - case STATE_AUTH_PLAIN: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_AUTH_LOGIN: - if (strncmp(line, "334 ", 4)) - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_AUTH_LOGIN_USER); - break; - - case STATE_AUTH_LOGIN_USER: - if (strncmp(line, "334 ", 4)) - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_AUTH_LOGIN_PASS); - break; - - case STATE_AUTH_LOGIN_PASS: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_MAIL: - if (line[0] != '2') { - smtp_client_mail_status(proto, line); - smtp_client_state(proto, STATE_RSET); - } - else - smtp_client_state(proto, STATE_RCPT); - break; - - case STATE_RCPT: - rcpt = &proto->mail->rcpt[proto->rcptidx++]; - if (line[0] != '2') - smtp_client_rcpt_status(proto, rcpt, line); - else { - proto->rcptok++; - smtp_client_state(proto, STATE_RCPT); - } - break; - - case STATE_DATA: - if (line[0] != '2' && line[0] != '3') { - smtp_client_mail_status(proto, line); - smtp_client_state(proto, STATE_RSET); - } - else - smtp_client_state(proto, STATE_BODY); - break; - - case STATE_EOM: - if (proto->params.lmtp) { - /* - * LMTP reports a status of each accepted RCPT. - * Report status for the first pending RCPT and read - * more lines if another rcpt needs a status. - */ - for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) { - rcpt = &proto->mail->rcpt[i]; - if (rcpt->done) - continue; - if (seen) { - io_set_read(proto->io); - return; - } - smtp_client_rcpt_status(proto, - &proto->mail->rcpt[i], line); - seen = 1; - } - } - smtp_client_mail_status(proto, line); - smtp_client_state(proto, STATE_READY); - break; - - case STATE_RSET: - if (line[0] != '2') - smtp_client_cancel(proto, FAIL_RESP, line); - else - smtp_client_state(proto, STATE_READY); - break; - - case STATE_QUIT: - smtp_client_free(proto); - break; - - default: - fatalx("%s: bad state %d", __func__, proto->state); - } -} - -static void -smtp_client_io(struct io *io, int evt, void *arg) -{ - struct smtp_client *proto = arg; - - log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io)); - - switch (evt) { - case IO_CONNECTED: - if (proto->params.tls_req == TLS_SMTPS) { - io_set_write(io); - smtp_require_tls(proto->tag, proto); - } - else - smtp_client_state(proto, STATE_BANNER); - break; - - case IO_TLSREADY: - proto->flags |= FLAG_TLS; - io_pause(proto->io, IO_IN); - smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io)); - break; - - case IO_DATAIN: - while (smtp_client_readline(proto)) - ; - break; - - case IO_LOWAT: - if (proto->state == STATE_BODY) - smtp_client_sendbody(proto); - else - io_set_read(io); - break; - - case IO_TIMEOUT: - errno = ETIMEDOUT; - smtp_client_abort(proto, FAIL_CONN, "Connection timeout"); - break; - - case IO_ERROR: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - case IO_TLSERROR: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - case IO_DISCONNECTED: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - default: - fatalx("%s: bad event %d", __func__, evt); - } -} - -/* - * return 1 if a new line is expected. - */ -static int -smtp_client_readline(struct smtp_client *proto) -{ - const char *e; - size_t len; - char *line, *msg, *p; - int cont; - - line = io_getline(proto->io, &len); - if (line == NULL) { - if (io_datalen(proto->io) >= proto->params.linemax) - smtp_client_abort(proto, FAIL_PROTO, "Line too long"); - return 0; - } - - /* Strip trailing '\r' */ - if (len && line[len - 1] == '\r') - line[--len] = '\0'; - - log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line); - - /* Validate SMTP */ - if (len > 3) { - msg = line + 4; - cont = (line[3] == '-'); - } else if (len == 3) { - msg = line + 3; - cont = 0; - } else { - smtp_client_abort(proto, FAIL_PROTO, "Response too short"); - return 0; - } - - /* Validate reply code. */ - if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) || - !isdigit((unsigned char)line[2])) { - smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code"); - return 0; - } - - /* Validate reply message. */ - for (p = msg; *p; p++) - if (!isprint((unsigned char)*p)) { - smtp_client_abort(proto, FAIL_PROTO, - "Non-printable characters in response"); - return 0; - } - - /* Read extensions. */ - if (proto->state == STATE_EHLO) { - if (strcmp(msg, "STARTTLS") == 0) - proto->ext |= SMTP_EXT_STARTTLS; - else if (strncmp(msg, "AUTH ", 5) == 0) { - proto->ext |= SMTP_EXT_AUTH; - if ((p = strstr(msg, " PLAIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - proto->ext |= SMTP_EXT_AUTH_PLAIN; - if ((p = strstr(msg, " LOGIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - proto->ext |= SMTP_EXT_AUTH_LOGIN; - } - else if (strcmp(msg, "PIPELINING") == 0) - proto->ext |= SMTP_EXT_PIPELINING; - else if (strcmp(msg, "DSN") == 0) - proto->ext |= SMTP_EXT_DSN; - else if (strncmp(msg, "SIZE ", 5) == 0) { - proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e); - if (e == NULL) - proto->ext |= SMTP_EXT_SIZE; - } - } - - if (smtp_client_replycat(proto, line) == -1) { - smtp_client_abort(proto, FAIL_INTERNAL, NULL); - return 0; - } - - if (cont) - return 1; - - if (io_datalen(proto->io)) { - /* - * There should be no pending data after a response is read, - * except for the multiple status lines after a LMTP message. - * It can also happen with pipelineing, but we don't do that - * for now. - */ - if (!(proto->params.lmtp && proto->state == STATE_EOM)) { - smtp_client_abort(proto, FAIL_PROTO, "Trailing data"); - return 0; - } - } - - io_set_write(proto->io); - smtp_client_response(proto, proto->reply); - return 0; -} - -/* - * Concatenate the given response line. - */ -static int -smtp_client_replycat(struct smtp_client *proto, const char *line) -{ - size_t len; - char *tmp; - int first; - - if (proto->reply && proto->reply[0]) { - /* - * If the line is the continuation of an multi-line response, - * skip the status and ESC parts. First, skip the status, then - * skip the separator amd ESC if found. - */ - first = 0; - line += 3; - if (line[0]) { - line += 1; - if (isdigit((unsigned char)line[0]) && line[1] == '.' && - isdigit((unsigned char)line[2]) && line[3] == '.' && - isdigit((unsigned char)line[4]) && - isspace((unsigned char)line[5])) - line += 5; - } - } else - first = 1; - - if (proto->reply) { - len = strlcat(proto->reply, line, proto->replysz); - if (len < proto->replysz) - return 0; - } - else - len = strlen(line); - - if (len > proto->params.ibufmax) { - errno = EMSGSIZE; - return -1; - } - - /* Allocate by multiples of 2^8 */ - len += (len % 256) ? (256 - (len % 256)) : 0; - - tmp = realloc(proto->reply, len); - if (tmp == NULL) - return -1; - if (proto->reply == NULL) - tmp[0] = '\0'; - - proto->reply = tmp; - proto->replysz = len; - (void)strlcat(proto->reply, line, proto->replysz); - - /* Replace the separator with a space for the first line. */ - if (first && proto->reply[3]) - proto->reply[3] = ' '; - - return 0; -} - -static void -smtp_client_sendbody(struct smtp_client *proto) -{ - ssize_t len; - size_t sz = 0, total, w; - char *ln = NULL; - int n; - - total = io_queued(proto->io); - w = 0; - - while (total < proto->params.obufmax) { - if ((len = getline(&ln, &sz, proto->mail->fp)) == -1) - break; - if (ln[len - 1] == '\n') - ln[len - 1] = '\0'; - n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln); - if (n == -1) { - free(ln); - smtp_client_abort(proto, FAIL_INTERNAL, NULL); - return; - } - total += n; - w += n; - } - free(ln); - - if (ferror(proto->mail->fp)) { - smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message"); - return; - } - - log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w); - - if (feof(proto->mail->fp)) - smtp_client_state(proto, STATE_EOM); -} - -static void -smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...) -{ - va_list ap; - char *p; - int len; - - va_start(ap, fmt); - len = vasprintf(&p, fmt, ap); - va_end(ap); - - if (len == -1) { - smtp_client_abort(proto, FAIL_INTERNAL, NULL); - return; - } - - log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p); - - len = io_printf(proto->io, "%s\r\n", p); - free(p); - - if (len == -1) - smtp_client_abort(proto, FAIL_INTERNAL, NULL); -} - -static void -smtp_client_mail_status(struct smtp_client *proto, const char *status) -{ - int i; - - for (i = 0; i < proto->mail->rcptcount; i++) - smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status); - - smtp_done(proto->tag, proto, proto->mail); - proto->mail = NULL; -} - -static void -smtp_client_mail_abort(struct smtp_client *proto) -{ - smtp_done(proto->tag, proto, proto->mail); - proto->mail = NULL; -} - -static void -smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line) -{ - struct smtp_status status; - - if (rcpt->done) - return; - - rcpt->done = 1; - status.rcpt = rcpt; - status.cmd = strstate[proto->state]; - status.status = line; - smtp_status(proto->tag, proto, &status); -} diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c deleted file mode 100644 index aefce155..00000000 --- a/smtpd/smtp_session.c +++ /dev/null @@ -1,3223 +0,0 @@ -/* $OpenBSD: smtp_session.c,v 1.426 2020/04/24 11:34:07 eric Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/uio.h> - -#include <netinet/in.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <limits.h> -#include <inttypes.h> -#include <openssl/ssl.h> -#include <resolv.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) -#include <vis.h> -#else -#include "bsd-vis.h" -#endif - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" -#include "rfc5322.h" - -#define SMTP_LINE_MAX 65535 -#define DATA_HIWAT 65535 -#define APPEND_DOMAIN_BUFFER_SIZE SMTP_LINE_MAX - -enum smtp_state { - STATE_NEW = 0, - STATE_CONNECTED, - STATE_TLS, - STATE_HELO, - STATE_AUTH_INIT, - STATE_AUTH_USERNAME, - STATE_AUTH_PASSWORD, - STATE_AUTH_FINALIZE, - STATE_BODY, - STATE_QUIT, -}; - -enum session_flags { - SF_EHLO = 0x0001, - SF_8BITMIME = 0x0002, - SF_SECURE = 0x0004, - SF_AUTHENTICATED = 0x0008, - SF_BOUNCE = 0x0010, - SF_VERIFIED = 0x0020, - SF_BADINPUT = 0x0080, -}; - -enum { - TX_OK = 0, - TX_ERROR_ENVELOPE, - TX_ERROR_SIZE, - TX_ERROR_IO, - TX_ERROR_LOOP, - TX_ERROR_MALFORMED, - TX_ERROR_RESOURCES, - TX_ERROR_INTERNAL, -}; - -enum smtp_command { - CMD_HELO = 0, - CMD_EHLO, - CMD_STARTTLS, - CMD_AUTH, - CMD_MAIL_FROM, - CMD_RCPT_TO, - CMD_DATA, - CMD_RSET, - CMD_QUIT, - CMD_HELP, - CMD_WIZ, - CMD_NOOP, - CMD_COMMIT, -}; - -struct smtp_rcpt { - TAILQ_ENTRY(smtp_rcpt) entry; - uint64_t evpid; - struct mailaddr maddr; - size_t destcount; -}; - -struct smtp_tx { - struct smtp_session *session; - uint32_t msgid; - - struct envelope evp; - size_t rcptcount; - size_t destcount; - TAILQ_HEAD(, smtp_rcpt) rcpts; - - time_t time; - int error; - size_t datain; - size_t odatalen; - FILE *ofile; - struct io *filter; - struct rfc5322_parser *parser; - int rcvcount; - int has_date; - int has_message_id; - - uint8_t junk; -}; - -struct smtp_session { - uint64_t id; - struct io *io; - struct listener *listener; - void *ssl_ctx; - struct sockaddr_storage ss; - char rdns[HOST_NAME_MAX+1]; - char smtpname[HOST_NAME_MAX+1]; - int fcrdns; - - int flags; - enum smtp_state state; - - uint8_t banner_sent; - char helo[LINE_MAX]; - char cmd[LINE_MAX]; - char username[SMTPD_MAXMAILADDRSIZE]; - - size_t mailcount; - struct event pause; - - struct smtp_tx *tx; - - enum smtp_command last_cmd; - enum filter_phase filter_phase; - const char *filter_param; - - uint8_t junk; -}; - -#define ADVERTISE_TLS(s) \ - ((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE)) - -#define ADVERTISE_AUTH(s) \ - ((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \ - !((s)->flags & SF_AUTHENTICATED)) - -#define ADVERTISE_EXT_DSN(s) \ - ((s)->listener->flags & F_EXT_DSN) - -#define SESSION_FILTERED(s) \ - ((s)->listener->flags & F_FILTERED) - -#define SESSION_DATA_FILTERED(s) \ - ((s)->listener->flags & F_FILTERED) - - -static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *); -static void smtp_session_init(void); -static void smtp_lookup_servername(struct smtp_session *); -static void smtp_getnameinfo_cb(void *, int, const char *, const char *); -static void smtp_getaddrinfo_cb(void *, int, struct addrinfo *); -static void smtp_connected(struct smtp_session *); -static void smtp_send_banner(struct smtp_session *); -static void smtp_tls_verified(struct smtp_session *); -static void smtp_io(struct io *, int, void *); -static void smtp_enter_state(struct smtp_session *, int); -static void smtp_reply(struct smtp_session *, char *, ...); -static void smtp_command(struct smtp_session *, char *); -static void smtp_rfc4954_auth_plain(struct smtp_session *, char *); -static void smtp_rfc4954_auth_login(struct smtp_session *, char *); -static void smtp_free(struct smtp_session *, const char *); -static const char *smtp_strstate(int); -static void smtp_cert_init(struct smtp_session *); -static void smtp_cert_init_cb(void *, int, const char *, const void *, size_t); -static void smtp_cert_verify(struct smtp_session *); -static void smtp_cert_verify_cb(void *, int); -static void smtp_auth_failure_pause(struct smtp_session *); -static void smtp_auth_failure_resume(int, short, void *); - -static int smtp_tx(struct smtp_session *); -static void smtp_tx_free(struct smtp_tx *); -static void smtp_tx_create_message(struct smtp_tx *); -static void smtp_tx_mail_from(struct smtp_tx *, const char *); -static void smtp_tx_rcpt_to(struct smtp_tx *, const char *); -static void smtp_tx_open_message(struct smtp_tx *); -static void smtp_tx_commit(struct smtp_tx *); -static void smtp_tx_rollback(struct smtp_tx *); -static int smtp_tx_dataline(struct smtp_tx *, const char *); -static int smtp_tx_filtered_dataline(struct smtp_tx *, const char *); -static void smtp_tx_eom(struct smtp_tx *); -static void smtp_filter_fd(struct smtp_tx *, int); -static int smtp_message_fd(struct smtp_tx *, int); -static void smtp_message_begin(struct smtp_tx *); -static void smtp_message_end(struct smtp_tx *); -static int smtp_filter_printf(struct smtp_tx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -static int smtp_message_printf(struct smtp_tx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); - -static int smtp_check_rset(struct smtp_session *, const char *); -static int smtp_check_helo(struct smtp_session *, const char *); -static int smtp_check_ehlo(struct smtp_session *, const char *); -static int smtp_check_auth(struct smtp_session *s, const char *); -static int smtp_check_starttls(struct smtp_session *, const char *); -static int smtp_check_mail_from(struct smtp_session *, const char *); -static int smtp_check_rcpt_to(struct smtp_session *, const char *); -static int smtp_check_data(struct smtp_session *, const char *); -static int smtp_check_noparam(struct smtp_session *, const char *); - -static void smtp_filter_phase(enum filter_phase, struct smtp_session *, const char *); - -static void smtp_proceed_connected(struct smtp_session *); -static void smtp_proceed_rset(struct smtp_session *, const char *); -static void smtp_proceed_helo(struct smtp_session *, const char *); -static void smtp_proceed_ehlo(struct smtp_session *, const char *); -static void smtp_proceed_auth(struct smtp_session *, const char *); -static void smtp_proceed_starttls(struct smtp_session *, const char *); -static void smtp_proceed_mail_from(struct smtp_session *, const char *); -static void smtp_proceed_rcpt_to(struct smtp_session *, const char *); -static void smtp_proceed_data(struct smtp_session *, const char *); -static void smtp_proceed_noop(struct smtp_session *, const char *); -static void smtp_proceed_help(struct smtp_session *, const char *); -static void smtp_proceed_wiz(struct smtp_session *, const char *); -static void smtp_proceed_quit(struct smtp_session *, const char *); -static void smtp_proceed_commit(struct smtp_session *, const char *); -static void smtp_proceed_rollback(struct smtp_session *, const char *); - -static void smtp_filter_begin(struct smtp_session *); -static void smtp_filter_end(struct smtp_session *); -static void smtp_filter_data_begin(struct smtp_session *); -static void smtp_filter_data_end(struct smtp_session *); - -static void smtp_report_link_connect(struct smtp_session *, const char *, int, - const struct sockaddr_storage *, - const struct sockaddr_storage *); -static void smtp_report_link_greeting(struct smtp_session *, const char *); -static void smtp_report_link_identify(struct smtp_session *, const char *, const char *); -static void smtp_report_link_tls(struct smtp_session *, const char *); -static void smtp_report_link_disconnect(struct smtp_session *); -static void smtp_report_link_auth(struct smtp_session *, const char *, const char *); -static void smtp_report_tx_reset(struct smtp_session *, uint32_t); -static void smtp_report_tx_begin(struct smtp_session *, uint32_t); -static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int); -static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int); -static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t); -static void smtp_report_tx_data(struct smtp_session *, uint32_t, int); -static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t); -static void smtp_report_tx_rollback(struct smtp_session *, uint32_t); -static void smtp_report_protocol_client(struct smtp_session *, const char *); -static void smtp_report_protocol_server(struct smtp_session *, const char *); -static void smtp_report_filter_response(struct smtp_session *, int, int, const char *); -static void smtp_report_timeout(struct smtp_session *); - - -/* musl work-around */ -void portable_freeaddrinfo(struct addrinfo *); - - -static struct { - int code; - enum filter_phase filter_phase; - const char *cmd; - - int (*check)(struct smtp_session *, const char *); - void (*proceed)(struct smtp_session *, const char *); -} commands[] = { - { CMD_HELO, FILTER_HELO, "HELO", smtp_check_helo, smtp_proceed_helo }, - { CMD_EHLO, FILTER_EHLO, "EHLO", smtp_check_ehlo, smtp_proceed_ehlo }, - { CMD_STARTTLS, FILTER_STARTTLS, "STARTTLS", smtp_check_starttls, smtp_proceed_starttls }, - { CMD_AUTH, FILTER_AUTH, "AUTH", smtp_check_auth, smtp_proceed_auth }, - { CMD_MAIL_FROM, FILTER_MAIL_FROM, "MAIL FROM", smtp_check_mail_from, smtp_proceed_mail_from }, - { CMD_RCPT_TO, FILTER_RCPT_TO, "RCPT TO", smtp_check_rcpt_to, smtp_proceed_rcpt_to }, - { CMD_DATA, FILTER_DATA, "DATA", smtp_check_data, smtp_proceed_data }, - { CMD_RSET, FILTER_RSET, "RSET", smtp_check_rset, smtp_proceed_rset }, - { CMD_QUIT, FILTER_QUIT, "QUIT", smtp_check_noparam, smtp_proceed_quit }, - { CMD_NOOP, FILTER_NOOP, "NOOP", smtp_check_noparam, smtp_proceed_noop }, - { CMD_HELP, FILTER_HELP, "HELP", smtp_check_noparam, smtp_proceed_help }, - { CMD_WIZ, FILTER_WIZ, "WIZ", smtp_check_noparam, smtp_proceed_wiz }, - { CMD_COMMIT, FILTER_COMMIT, ".", smtp_check_noparam, smtp_proceed_commit }, - { -1, 0, NULL, NULL }, -}; - -static struct tree wait_lka_helo; -static struct tree wait_lka_mail; -static struct tree wait_lka_rcpt; -static struct tree wait_parent_auth; -static struct tree wait_queue_msg; -static struct tree wait_queue_fd; -static struct tree wait_queue_commit; -static struct tree wait_ssl_init; -static struct tree wait_ssl_verify; -static struct tree wait_filters; -static struct tree wait_filter_fd; - -static void -header_append_domain_buffer(char *buffer, char *domain, size_t len) -{ - size_t i; - int escape, quote, comment, bracket; - int has_domain, has_bracket, has_group; - int pos_bracket, pos_component, pos_insert; - char copy[APPEND_DOMAIN_BUFFER_SIZE]; - - escape = quote = comment = bracket = 0; - has_domain = has_bracket = has_group = 0; - pos_bracket = pos_insert = pos_component = 0; - for (i = 0; buffer[i]; ++i) { - if (buffer[i] == '(' && !escape && !quote) - comment++; - if (buffer[i] == '"' && !escape && !comment) - quote = !quote; - if (buffer[i] == ')' && !escape && !quote && comment) - comment--; - if (buffer[i] == '\\' && !escape && !comment && !quote) - escape = 1; - else - escape = 0; - if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { - bracket++; - has_bracket = 1; - } - if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { - bracket--; - pos_bracket = i; - } - if (buffer[i] == '@' && !escape && !comment && !quote) - has_domain = 1; - if (buffer[i] == ':' && !escape && !comment && !quote) - has_group = 1; - - /* update insert point if not in comment and not on a whitespace */ - if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i])) - pos_component = i; - } - - /* parse error, do not attempt to modify */ - if (escape || quote || comment || bracket) - return; - - /* domain already present, no need to modify */ - if (has_domain) - return; - - /* address is group, skip */ - if (has_group) - return; - - /* there's an address between brackets, just append domain */ - if (has_bracket) { - pos_bracket--; - while (isspace((unsigned char)buffer[pos_bracket])) - pos_bracket--; - if (buffer[pos_bracket] == '<') - return; - pos_insert = pos_bracket + 1; - } - else { - /* otherwise append address to last component */ - pos_insert = pos_component + 1; - - /* empty address */ - if (buffer[pos_component] == '\0' || - isspace((unsigned char)buffer[pos_component])) - return; - } - - if (snprintf(copy, sizeof copy, "%.*s@%s%s", - (int)pos_insert, buffer, - domain, - buffer+pos_insert) >= (int)sizeof copy) - return; - - memcpy(buffer, copy, len); -} - -static void -header_address_rewrite_buffer(char *buffer, const char *address, size_t len) -{ - size_t i; - int address_len; - int escape, quote, comment, bracket; - int has_bracket, has_group; - int pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end; - int insert_beg, insert_end; - char copy[APPEND_DOMAIN_BUFFER_SIZE]; - - escape = quote = comment = bracket = 0; - has_bracket = has_group = 0; - pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0; - for (i = 0; buffer[i]; ++i) { - if (buffer[i] == '(' && !escape && !quote) - comment++; - if (buffer[i] == '"' && !escape && !comment) - quote = !quote; - if (buffer[i] == ')' && !escape && !quote && comment) - comment--; - if (buffer[i] == '\\' && !escape && !comment && !quote) - escape = 1; - else - escape = 0; - if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { - bracket++; - has_bracket = 1; - pos_bracket_beg = i+1; - } - if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { - bracket--; - pos_bracket_end = i; - } - if (buffer[i] == ':' && !escape && !comment && !quote) - has_group = 1; - - /* update insert point if not in comment and not on a whitespace */ - if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i])) - pos_component_end = i; - } - - /* parse error, do not attempt to modify */ - if (escape || quote || comment || bracket) - return; - - /* address is group, skip */ - if (has_group) - return; - - /* there's an address between brackets, just replace everything brackets */ - if (has_bracket) { - insert_beg = pos_bracket_beg; - insert_end = pos_bracket_end; - } - else { - if (pos_component_end == 0) - pos_component_beg = 0; - else { - for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg) - if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg])) - break; - pos_component_beg += 1; - pos_component_end += 1; - } - insert_beg = pos_component_beg; - insert_end = pos_component_end; - } - - /* check that masquerade won' t overflow */ - address_len = strlen(address); - if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len) - return; - - (void)strlcpy(copy, buffer, sizeof copy); - (void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg); - (void)strlcat(copy, buffer+insert_end, sizeof (copy)); - memcpy(buffer, copy, len); -} - -static void -header_domain_append_callback(struct smtp_tx *tx, const char *hdr, - const char *val) -{ - size_t i, j, linelen; - int escape, quote, comment, skip; - char buffer[APPEND_DOMAIN_BUFFER_SIZE]; - const char *line, *end; - - if (smtp_message_printf(tx, "%s:", hdr) == -1) - return; - - j = 0; - escape = quote = comment = skip = 0; - memset(buffer, 0, sizeof buffer); - - for (line = val; line; line = end) { - end = strchr(line, '\n'); - if (end) { - linelen = end - line; - end++; - } - else - linelen = strlen(line); - - for (i = 0; i < linelen; ++i) { - if (line[i] == '(' && !escape && !quote) - comment++; - if (line[i] == '"' && !escape && !comment) - quote = !quote; - if (line[i] == ')' && !escape && !quote && comment) - comment--; - if (line[i] == '\\' && !escape && !comment && !quote) - escape = 1; - else - escape = 0; - - /* found a separator, buffer contains a full address */ - if (line[i] == ',' && !escape && !quote && !comment) { - if (!skip && j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) { - header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer); - if (tx->session->flags & SF_AUTHENTICATED && - tx->session->listener->sendertable[0] && - tx->session->listener->flags & F_MASQUERADE && - !(strcasecmp(hdr, "From"))) - header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender), - sizeof buffer); - } - if (smtp_message_printf(tx, "%s,", buffer) == -1) - return; - j = 0; - skip = 0; - memset(buffer, 0, sizeof buffer); - } - else { - if (skip) { - if (smtp_message_printf(tx, "%c", line[i]) == -1) - return; - } - else { - buffer[j++] = line[i]; - if (j == sizeof (buffer) - 1) { - if (smtp_message_printf(tx, "%s", buffer) == -1) - return; - skip = 1; - j = 0; - memset(buffer, 0, sizeof buffer); - } - } - } - } - if (skip) { - if (smtp_message_printf(tx, "\n") == -1) - return; - } - else { - buffer[j++] = '\n'; - if (j == sizeof (buffer) - 1) { - if (smtp_message_printf(tx, "%s", buffer) == -1) - return; - skip = 1; - j = 0; - memset(buffer, 0, sizeof buffer); - } - } - } - - /* end of header, if buffer is not empty we'll process it */ - if (buffer[0]) { - if (j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) { - header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer); - if (tx->session->flags & SF_AUTHENTICATED && - tx->session->listener->sendertable[0] && - tx->session->listener->flags & F_MASQUERADE && - !(strcasecmp(hdr, "From"))) - header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender), - sizeof buffer); - } - smtp_message_printf(tx, "%s", buffer); - } -} - -static void -smtp_session_init(void) -{ - static int init = 0; - - if (!init) { - tree_init(&wait_lka_helo); - tree_init(&wait_lka_mail); - tree_init(&wait_lka_rcpt); - tree_init(&wait_parent_auth); - tree_init(&wait_queue_msg); - tree_init(&wait_queue_fd); - tree_init(&wait_queue_commit); - tree_init(&wait_ssl_init); - tree_init(&wait_ssl_verify); - tree_init(&wait_filters); - tree_init(&wait_filter_fd); - init = 1; - } -} - -int -smtp_session(struct listener *listener, int sock, - const struct sockaddr_storage *ss, const char *hostname, struct io *io) -{ - struct smtp_session *s; - - smtp_session_init(); - - if ((s = calloc(1, sizeof(*s))) == NULL) - return (-1); - - s->id = generate_uid(); - s->listener = listener; - memmove(&s->ss, ss, sizeof(*ss)); - - if (io != NULL) - s->io = io; - else - s->io = io_new(); - - io_set_callback(s->io, smtp_io, s); - io_set_fd(s->io, sock); - io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000); - io_set_write(s->io); - s->state = STATE_NEW; - - (void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname)); - - log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p " - "[hostname=%s, port=%d, tag=%s]", s, listener, - listener->hostname, ntohs(listener->port), listener->tag); - - /* For local enqueueing, the hostname is already set */ - if (hostname) { - s->flags |= SF_AUTHENTICATED; - /* A bit of a hack */ - if (!strcmp(hostname, "localhost")) - s->flags |= SF_BOUNCE; - (void)strlcpy(s->rdns, hostname, sizeof(s->rdns)); - s->fcrdns = 1; - smtp_lookup_servername(s); - } else { - resolver_getnameinfo((struct sockaddr *)&s->ss, NI_NAMEREQD, - smtp_getnameinfo_cb, s); - } - - /* session may have been freed by now */ - - return (0); -} - -static void -smtp_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv) -{ - struct smtp_session *s = arg; - struct addrinfo hints; - - if (gaierrno) { - (void)strlcpy(s->rdns, "<unknown>", sizeof(s->rdns)); - - if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME) - s->fcrdns = 0; - else { - log_warnx("getnameinfo: %s: %s", ss_to_text(&s->ss), - gai_strerror(gaierrno)); - s->fcrdns = -1; - } - - smtp_lookup_servername(s); - return; - } - - (void)strlcpy(s->rdns, host, sizeof(s->rdns)); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = s->ss.ss_family; - hints.ai_socktype = SOCK_STREAM; - resolver_getaddrinfo(s->rdns, NULL, &hints, smtp_getaddrinfo_cb, s); -} - -static void -smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0) -{ - struct smtp_session *s = arg; - struct addrinfo *ai; - char fwd[64], rev[64]; - - if (gaierrno) { - if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME) - s->fcrdns = 0; - else { - log_warnx("getaddrinfo: %s: %s", s->rdns, - gai_strerror(gaierrno)); - s->fcrdns = -1; - } - } - else { - strlcpy(rev, ss_to_text(&s->ss), sizeof(rev)); - for (ai = ai0; ai; ai = ai->ai_next) { - strlcpy(fwd, sa_to_text(ai->ai_addr), sizeof(fwd)); - if (!strcmp(fwd, rev)) { - s->fcrdns = 1; - break; - } - } - portable_freeaddrinfo(ai0); - } - - smtp_lookup_servername(s); -} - -void -smtp_session_imsg(struct mproc *p, struct imsg *imsg) -{ - struct smtp_session *s; - struct smtp_rcpt *rcpt; - char user[SMTPD_MAXMAILADDRSIZE]; - char tmp[SMTP_LINE_MAX]; - struct msg m; - const char *line, *helo; - uint64_t reqid, evpid; - uint32_t msgid; - int status, success; - int filter_response; - const char *filter_param; - uint8_t i; - - switch (imsg->hdr.type) { - - case IMSG_SMTP_CHECK_SENDER: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &status); - m_end(&m); - s = tree_xpop(&wait_lka_mail, reqid); - switch (status) { - case LKA_OK: - smtp_tx_create_message(s->tx); - break; - - case LKA_PERMFAIL: - smtp_tx_free(s->tx); - smtp_reply(s, "%d %s", 530, "Sender rejected"); - break; - case LKA_TEMPFAIL: - smtp_tx_free(s->tx); - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - break; - } - return; - - case IMSG_SMTP_EXPAND_RCPT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &status); - m_get_string(&m, &line); - m_end(&m); - s = tree_xpop(&wait_lka_rcpt, reqid); - - tmp[0] = '\0'; - if (s->tx->evp.rcpt.user[0]) { - (void)strlcpy(tmp, s->tx->evp.rcpt.user, sizeof tmp); - if (s->tx->evp.rcpt.domain[0]) { - (void)strlcat(tmp, "@", sizeof tmp); - (void)strlcat(tmp, s->tx->evp.rcpt.domain, - sizeof tmp); - } - } - - switch (status) { - case LKA_OK: - fatalx("unexpected ok"); - case LKA_PERMFAIL: - smtp_reply(s, "%s: <%s>", line, tmp); - break; - case LKA_TEMPFAIL: - smtp_reply(s, "%s: <%s>", line, tmp); - break; - } - return; - - case IMSG_SMTP_LOOKUP_HELO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - s = tree_xpop(&wait_lka_helo, reqid); - m_get_int(&m, &status); - if (status == LKA_OK) { - m_get_string(&m, &helo); - (void)strlcpy(s->smtpname, helo, sizeof(s->smtpname)); - } - m_end(&m); - smtp_connected(s); - return; - - case IMSG_SMTP_MESSAGE_CREATE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - s = tree_xpop(&wait_queue_msg, reqid); - if (success) { - m_get_msgid(&m, &msgid); - s->tx->msgid = msgid; - s->tx->evp.id = msgid_to_evpid(msgid); - s->tx->rcptcount = 0; - smtp_reply(s, "250 %s Ok", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); - } else { - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_tx_free(s->tx); - smtp_enter_state(s, STATE_QUIT); - } - m_end(&m); - return; - - case IMSG_SMTP_MESSAGE_OPEN: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - m_end(&m); - - s = tree_xpop(&wait_queue_fd, reqid); - if (!success || imsg->fd == -1) { - if (imsg->fd != -1) - close(imsg->fd); - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_enter_state(s, STATE_QUIT); - return; - } - - log_debug("smtp: %p: fd %d from queue", s, imsg->fd); - - if (smtp_message_fd(s->tx, imsg->fd)) { - if (!SESSION_DATA_FILTERED(s)) - smtp_message_begin(s->tx); - else - smtp_filter_data_begin(s); - } - return; - - case IMSG_FILTER_SMTP_DATA_BEGIN: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - m_end(&m); - - s = tree_xpop(&wait_filter_fd, reqid); - if (!success || imsg->fd == -1) { - if (imsg->fd != -1) - close(imsg->fd); - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_enter_state(s, STATE_QUIT); - return; - } - - log_debug("smtp: %p: fd %d from lka", s, imsg->fd); - - smtp_filter_fd(s->tx, imsg->fd); - smtp_message_begin(s->tx); - return; - - case IMSG_QUEUE_ENVELOPE_SUBMIT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - s = tree_xget(&wait_lka_rcpt, reqid); - if (success) { - m_get_evpid(&m, &evpid); - s->tx->evp.id = evpid; - s->tx->destcount++; - smtp_report_tx_envelope(s, s->tx->msgid, evpid); - } - else - s->tx->error = TX_ERROR_ENVELOPE; - m_end(&m); - return; - - case IMSG_QUEUE_ENVELOPE_COMMIT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - m_end(&m); - if (!success) - fatalx("commit evp failed: not supposed to happen"); - s = tree_xpop(&wait_lka_rcpt, reqid); - if (s->tx->error) { - /* - * If an envelope failed, we can't cancel the last - * RCPT only so we must cancel the whole transaction - * and close the connection. - */ - smtp_reply(s, "421 %s Temporary failure", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_enter_state(s, STATE_QUIT); - } - else { - rcpt = xcalloc(1, sizeof(*rcpt)); - rcpt->evpid = s->tx->evp.id; - rcpt->destcount = s->tx->destcount; - rcpt->maddr = s->tx->evp.rcpt; - TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry); - - s->tx->destcount = 0; - s->tx->rcptcount++; - smtp_reply(s, "250 %s %s: Recipient ok", - esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID), - esc_description(ESC_DESTINATION_ADDRESS_VALID)); - } - return; - - case IMSG_SMTP_MESSAGE_COMMIT: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - m_end(&m); - s = tree_xpop(&wait_queue_commit, reqid); - if (!success) { - smtp_reply(s, "421 %s Temporary failure", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_tx_free(s->tx); - smtp_enter_state(s, STATE_QUIT); - return; - } - - smtp_reply(s, "250 %s %08x Message accepted for delivery", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS), - s->tx->msgid); - smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen); - smtp_report_tx_reset(s, s->tx->msgid); - - log_info("%016"PRIx64" smtp message " - "msgid=%08x size=%zu nrcpt=%zu proto=%s", - s->id, - s->tx->msgid, - s->tx->odatalen, - s->tx->rcptcount, - s->flags & SF_EHLO ? "ESMTP" : "SMTP"); - TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) { - log_info("%016"PRIx64" smtp envelope " - "evpid=%016"PRIx64" from=<%s%s%s> to=<%s%s%s>", - s->id, - rcpt->evpid, - s->tx->evp.sender.user, - s->tx->evp.sender.user[0] == '\0' ? "" : "@", - s->tx->evp.sender.domain, - rcpt->maddr.user, - rcpt->maddr.user[0] == '\0' ? "" : "@", - rcpt->maddr.domain); - } - smtp_tx_free(s->tx); - s->mailcount++; - smtp_enter_state(s, STATE_HELO); - return; - - case IMSG_SMTP_AUTHENTICATE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &success); - m_end(&m); - - s = tree_xpop(&wait_parent_auth, reqid); - strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE); - if (success == LKA_OK) { - log_info("%016"PRIx64" smtp " - "authentication user=%s " - "result=ok", - s->id, user); - s->flags |= SF_AUTHENTICATED; - smtp_report_link_auth(s, user, "pass"); - smtp_reply(s, "235 %s Authentication succeeded", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); - } - else if (success == LKA_PERMFAIL) { - log_info("%016"PRIx64" smtp " - "authentication user=%s " - "result=permfail", - s->id, user); - smtp_report_link_auth(s, user, "fail"); - smtp_auth_failure_pause(s); - return; - } - else if (success == LKA_TEMPFAIL) { - log_info("%016"PRIx64" smtp " - "authentication user=%s " - "result=tempfail", - s->id, user); - smtp_report_link_auth(s, user, "error"); - smtp_reply(s, "421 %s Temporary failure", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - } - else - fatalx("bad lka response"); - - smtp_enter_state(s, STATE_HELO); - return; - - case IMSG_FILTER_SMTP_PROTOCOL: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &filter_response); - if (filter_response != FILTER_PROCEED && - filter_response != FILTER_JUNK) - m_get_string(&m, &filter_param); - else - filter_param = NULL; - m_end(&m); - - s = tree_xpop(&wait_filters, reqid); - - switch (filter_response) { - case FILTER_REJECT: - case FILTER_DISCONNECT: - if (!valid_smtp_response(filter_param) || - (filter_param[0] != '4' && filter_param[0] != '5')) - filter_param = "421 Internal server error"; - if (!strncmp(filter_param, "421", 3)) - filter_response = FILTER_DISCONNECT; - - smtp_report_filter_response(s, s->filter_phase, - filter_response, filter_param); - - smtp_reply(s, "%s", filter_param); - - if (filter_response == FILTER_DISCONNECT) - smtp_enter_state(s, STATE_QUIT); - else if (s->filter_phase == FILTER_COMMIT) - smtp_proceed_rollback(s, NULL); - break; - - - case FILTER_JUNK: - if (s->tx) - s->tx->junk = 1; - else - s->junk = 1; - /* fallthrough */ - - case FILTER_PROCEED: - filter_param = s->filter_param; - /* fallthrough */ - - case FILTER_REWRITE: - smtp_report_filter_response(s, s->filter_phase, - filter_response, - filter_param == s->filter_param ? NULL : filter_param); - if (s->filter_phase == FILTER_CONNECT) { - smtp_proceed_connected(s); - return; - } - for (i = 0; i < nitems(commands); ++i) - if (commands[i].filter_phase == s->filter_phase) { - if (filter_response == FILTER_REWRITE) - if (!commands[i].check(s, filter_param)) - break; - commands[i].proceed(s, filter_param); - break; - } - break; - } - return; - } - - log_warnx("smtp_session_imsg: unexpected %s imsg", - imsg_to_str(imsg->hdr.type)); - fatalx(NULL); -} - -static void -smtp_tls_verified(struct smtp_session *s) -{ - X509 *x; - - x = SSL_get_peer_certificate(io_tls(s->io)); - if (x) { - log_info("%016"PRIx64" smtp " - "client-cert-check result=\"%s\"", - s->id, - (s->flags & SF_VERIFIED) ? "success" : "failure"); - X509_free(x); - } - - if (s->listener->flags & F_SMTPS) { - stat_increment("smtp.smtps", 1); - io_set_write(s->io); - smtp_send_banner(s); - } - else { - stat_increment("smtp.tls", 1); - smtp_enter_state(s, STATE_HELO); - } -} - -static void -smtp_io(struct io *io, int evt, void *arg) -{ - struct smtp_session *s = arg; - char *line; - size_t len; - int eom; - - log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt), - io_strio(io)); - - switch (evt) { - - case IO_TLSREADY: - log_info("%016"PRIx64" smtp tls ciphers=%s", - s->id, ssl_to_text(io_tls(s->io))); - - smtp_report_link_tls(s, ssl_to_text(io_tls(s->io))); - - s->flags |= SF_SECURE; - s->helo[0] = '\0'; - - smtp_cert_verify(s); - break; - - case IO_DATAIN: - nextline: - line = io_getline(s->io, &len); - if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) || - (line && len >= SMTP_LINE_MAX)) { - s->flags |= SF_BADINPUT; - smtp_reply(s, "500 %s Line too long", - esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); - smtp_enter_state(s, STATE_QUIT); - io_set_write(io); - return; - } - - /* No complete line received */ - if (line == NULL) - return; - - /* Strip trailing '\r' */ - if (len && line[len - 1] == '\r') - line[--len] = '\0'; - - /* Message body */ - eom = 0; - if (s->state == STATE_BODY) { - if (strcmp(line, ".")) { - s->tx->datain += strlen(line) + 1; - if (s->tx->datain > env->sc_maxsize) - s->tx->error = TX_ERROR_SIZE; - } - eom = (s->tx->filter == NULL) ? - smtp_tx_dataline(s->tx, line) : - smtp_tx_filtered_dataline(s->tx, line); - if (eom == 0) - goto nextline; - } - - /* Pipelining not supported */ - if (io_datalen(s->io)) { - s->flags |= SF_BADINPUT; - smtp_reply(s, "500 %s %s: Pipelining not supported", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - smtp_enter_state(s, STATE_QUIT); - io_set_write(io); - return; - } - - if (eom) { - io_set_write(io); - if (s->tx->filter == NULL) - smtp_tx_eom(s->tx); - return; - } - - /* Must be a command */ - if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) { - s->flags |= SF_BADINPUT; - smtp_reply(s, "500 %s Command line too long", - esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); - smtp_enter_state(s, STATE_QUIT); - io_set_write(io); - return; - } - io_set_write(io); - smtp_command(s, line); - break; - - case IO_LOWAT: - if (s->state == STATE_QUIT) { - log_info("%016"PRIx64" smtp disconnected " - "reason=quit", - s->id); - smtp_free(s, "done"); - break; - } - - /* Wait for the client to start tls */ - if (s->state == STATE_TLS) { - smtp_cert_init(s); - break; - } - - io_set_read(io); - break; - - case IO_TIMEOUT: - log_info("%016"PRIx64" smtp disconnected " - "reason=timeout", - s->id); - smtp_report_timeout(s); - smtp_free(s, "timeout"); - break; - - case IO_DISCONNECTED: - log_info("%016"PRIx64" smtp disconnected " - "reason=disconnect", - s->id); - smtp_free(s, "disconnected"); - break; - - case IO_ERROR: - log_info("%016"PRIx64" smtp disconnected " - "reason=\"io-error: %s\"", - s->id, io_error(io)); - smtp_free(s, "IO error"); - break; - - default: - fatalx("smtp_io()"); - } -} - -static void -smtp_command(struct smtp_session *s, char *line) -{ - char *args; - int cmd, i; - - log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line); - - /* - * These states are special. - */ - if (s->state == STATE_AUTH_INIT) { - smtp_report_protocol_client(s, "********"); - smtp_rfc4954_auth_plain(s, line); - return; - } - if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) { - smtp_report_protocol_client(s, "********"); - smtp_rfc4954_auth_login(s, line); - return; - } - - if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0) - smtp_report_protocol_client(s, "AUTH PLAIN ********"); - else - smtp_report_protocol_client(s, line); - - - /* - * Unlike other commands, "mail from" and "rcpt to" contain a - * space in the command name. - */ - if (strncasecmp("mail from:", line, 10) == 0 || - strncasecmp("rcpt to:", line, 8) == 0) - args = strchr(line, ':'); - else - args = strchr(line, ' '); - - if (args) { - *args++ = '\0'; - while (isspace((unsigned char)*args)) - args++; - } - - cmd = -1; - for (i = 0; commands[i].code != -1; i++) - if (!strcasecmp(line, commands[i].cmd)) { - cmd = commands[i].code; - break; - } - - s->last_cmd = cmd; - switch (cmd) { - /* - * INIT - */ - case CMD_HELO: - if (!smtp_check_helo(s, args)) - break; - smtp_filter_phase(FILTER_HELO, s, args); - break; - - case CMD_EHLO: - if (!smtp_check_ehlo(s, args)) - break; - smtp_filter_phase(FILTER_EHLO, s, args); - break; - - /* - * SETUP - */ - case CMD_STARTTLS: - if (!smtp_check_starttls(s, args)) - break; - - smtp_filter_phase(FILTER_STARTTLS, s, NULL); - break; - - case CMD_AUTH: - if (!smtp_check_auth(s, args)) - break; - smtp_filter_phase(FILTER_AUTH, s, args); - break; - - case CMD_MAIL_FROM: - if (!smtp_check_mail_from(s, args)) - break; - smtp_filter_phase(FILTER_MAIL_FROM, s, args); - break; - - /* - * TRANSACTION - */ - case CMD_RCPT_TO: - if (!smtp_check_rcpt_to(s, args)) - break; - smtp_filter_phase(FILTER_RCPT_TO, s, args); - break; - - case CMD_RSET: - if (!smtp_check_rset(s, args)) - break; - smtp_filter_phase(FILTER_RSET, s, NULL); - break; - - case CMD_DATA: - if (!smtp_check_data(s, args)) - break; - smtp_filter_phase(FILTER_DATA, s, NULL); - break; - - /* - * ANY - */ - case CMD_QUIT: - if (!smtp_check_noparam(s, args)) - break; - smtp_filter_phase(FILTER_QUIT, s, NULL); - break; - - case CMD_NOOP: - if (!smtp_check_noparam(s, args)) - break; - smtp_filter_phase(FILTER_NOOP, s, NULL); - break; - - case CMD_HELP: - if (!smtp_check_noparam(s, args)) - break; - smtp_proceed_help(s, NULL); - break; - - case CMD_WIZ: - if (!smtp_check_noparam(s, args)) - break; - smtp_proceed_wiz(s, NULL); - break; - - default: - smtp_reply(s, "500 %s %s: Command unrecognized", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - break; - } -} - -static int -smtp_check_rset(struct smtp_session *s, const char *args) -{ - if (!smtp_check_noparam(s, args)) - return 0; - - if (s->helo[0] == '\0') { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - return 1; -} - -static int -smtp_check_helo(struct smtp_session *s, const char *args) -{ - if (!s->banner_sent) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->helo[0]) { - smtp_reply(s, "503 %s %s: Already identified", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (args == NULL) { - smtp_reply(s, "501 %s %s: HELO requires domain name", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (!valid_domainpart(args)) { - smtp_reply(s, "501 %s %s: Invalid domain name", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); - return 0; - } - - return 1; -} - -static int -smtp_check_ehlo(struct smtp_session *s, const char *args) -{ - if (!s->banner_sent) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->helo[0]) { - smtp_reply(s, "503 %s %s: Already identified", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (args == NULL) { - smtp_reply(s, "501 %s %s: EHLO requires domain name", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (!valid_domainpart(args)) { - smtp_reply(s, "501 %s %s: Invalid domain name", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); - return 0; - } - - return 1; -} - -static int -smtp_check_auth(struct smtp_session *s, const char *args) -{ - if (s->helo[0] == '\0' || s->tx) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->flags & SF_AUTHENTICATED) { - smtp_reply(s, "503 %s %s: Already authenticated", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (!ADVERTISE_AUTH(s)) { - smtp_reply(s, "503 %s %s: Command not supported", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (args == NULL) { - smtp_reply(s, "501 %s %s: No parameters given", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); - return 0; - } - - return 1; -} - -static int -smtp_check_starttls(struct smtp_session *s, const char *args) -{ - if (s->helo[0] == '\0' || s->tx) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (!(s->listener->flags & F_STARTTLS)) { - smtp_reply(s, "503 %s %s: Command not supported", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->flags & SF_SECURE) { - smtp_reply(s, "503 %s %s: Channel already secured", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (args != NULL) { - smtp_reply(s, "501 %s %s: No parameters allowed", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); - return 0; - } - - return 1; -} - -static int -smtp_check_mail_from(struct smtp_session *s, const char *args) -{ - char *copy; - char tmp[SMTP_LINE_MAX]; - struct mailaddr sender; - - (void)strlcpy(tmp, args, sizeof tmp); - copy = tmp; - - if (s->helo[0] == '\0' || s->tx) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->listener->flags & F_STARTTLS_REQUIRE && - !(s->flags & SF_SECURE)) { - smtp_reply(s, - "530 %s %s: Must issue a STARTTLS command first", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->listener->flags & F_AUTH_REQUIRE && - !(s->flags & SF_AUTHENTICATED)) { - smtp_reply(s, - "530 %s %s: Must issue an AUTH command first", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->mailcount >= env->sc_session_max_mails) { - /* we can pretend we had too many recipients */ - smtp_reply(s, "452 %s %s: Too many messages sent", - esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), - esc_description(ESC_TOO_MANY_RECIPIENTS)); - return 0; - } - - if (smtp_mailaddr(&sender, copy, 1, ©, - s->smtpname) == 0) { - smtp_reply(s, "553 %s Sender address syntax error", - esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); - return 0; - } - - return 1; -} - -static int -smtp_check_rcpt_to(struct smtp_session *s, const char *args) -{ - char *copy; - char tmp[SMTP_LINE_MAX]; - - (void)strlcpy(tmp, args, sizeof tmp); - copy = tmp; - - if (s->tx == NULL) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->tx->rcptcount >= env->sc_session_max_rcpt) { - smtp_reply(s->tx->session, "451 %s %s: Too many recipients", - esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), - esc_description(ESC_TOO_MANY_RECIPIENTS)); - return 0; - } - - if (smtp_mailaddr(&s->tx->evp.rcpt, copy, 0, ©, - s->tx->session->smtpname) == 0) { - smtp_reply(s->tx->session, - "501 %s Recipient address syntax error", - esc_code(ESC_STATUS_PERMFAIL, - ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX)); - return 0; - } - - return 1; -} - -static int -smtp_check_data(struct smtp_session *s, const char *args) -{ - if (!smtp_check_noparam(s, args)) - return 0; - - if (s->tx == NULL) { - smtp_reply(s, "503 %s %s: Command not allowed at this point.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); - return 0; - } - - if (s->tx->rcptcount == 0) { - smtp_reply(s, "503 %s %s: No recipient specified", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); - return 0; - } - - return 1; -} - -static int -smtp_check_noparam(struct smtp_session *s, const char *args) -{ - if (args != NULL) { - smtp_reply(s, "500 %s %s: command does not accept arguments.", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); - return 0; - } - return 1; -} - -static void -smtp_query_filters(enum filter_phase phase, struct smtp_session *s, const char *args) -{ - m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_int(p_lka, phase); - m_add_string(p_lka, args); - m_close(p_lka); - tree_xset(&wait_filters, s->id, s); -} - -static void -smtp_filter_begin(struct smtp_session *s) -{ - if (!SESSION_FILTERED(s)) - return; - - m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->listener->filter_name); - m_close(p_lka); -} - -static void -smtp_filter_end(struct smtp_session *s) -{ - if (!SESSION_FILTERED(s)) - return; - - m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1); - m_add_id(p_lka, s->id); - m_close(p_lka); -} - -static void -smtp_filter_data_begin(struct smtp_session *s) -{ - if (!SESSION_FILTERED(s)) - return; - - m_create(p_lka, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, -1); - m_add_id(p_lka, s->id); - m_close(p_lka); - tree_xset(&wait_filter_fd, s->id, s); -} - -static void -smtp_filter_data_end(struct smtp_session *s) -{ - if (!SESSION_FILTERED(s)) - return; - - if (s->tx->filter == NULL) - return; - - io_free(s->tx->filter); - s->tx->filter = NULL; - - m_create(p_lka, IMSG_FILTER_SMTP_DATA_END, 0, 0, -1); - m_add_id(p_lka, s->id); - m_close(p_lka); -} - -static void -smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *param) -{ - uint8_t i; - - s->filter_phase = phase; - s->filter_param = param; - - if (SESSION_FILTERED(s)) { - smtp_query_filters(phase, s, param ? param : ""); - return; - } - - if (s->filter_phase == FILTER_CONNECT) { - smtp_proceed_connected(s); - return; - } - - for (i = 0; i < nitems(commands); ++i) - if (commands[i].filter_phase == s->filter_phase) { - commands[i].proceed(s, param); - break; - } -} - -static void -smtp_proceed_rset(struct smtp_session *s, const char *args) -{ - smtp_reply(s, "250 %s Reset state", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); - - if (s->tx) { - if (s->tx->msgid) - smtp_tx_rollback(s->tx); - smtp_tx_free(s->tx); - } -} - -static void -smtp_proceed_helo(struct smtp_session *s, const char *args) -{ - (void)strlcpy(s->helo, args, sizeof(s->helo)); - s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED; - - smtp_report_link_identify(s, "HELO", s->helo); - - smtp_enter_state(s, STATE_HELO); - - smtp_reply(s, "250 %s Hello %s %s%s%s, pleased to meet you", - s->smtpname, - s->helo, - s->ss.ss_family == AF_INET6 ? "" : "[", - ss_to_text(&s->ss), - s->ss.ss_family == AF_INET6 ? "" : "]"); -} - -static void -smtp_proceed_ehlo(struct smtp_session *s, const char *args) -{ - (void)strlcpy(s->helo, args, sizeof(s->helo)); - s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED; - s->flags |= SF_EHLO; - s->flags |= SF_8BITMIME; - - smtp_report_link_identify(s, "EHLO", s->helo); - - smtp_enter_state(s, STATE_HELO); - smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you", - s->smtpname, - s->helo, - s->ss.ss_family == AF_INET6 ? "" : "[", - ss_to_text(&s->ss), - s->ss.ss_family == AF_INET6 ? "" : "]"); - - smtp_reply(s, "250-8BITMIME"); - smtp_reply(s, "250-ENHANCEDSTATUSCODES"); - smtp_reply(s, "250-SIZE %zu", env->sc_maxsize); - if (ADVERTISE_EXT_DSN(s)) - smtp_reply(s, "250-DSN"); - if (ADVERTISE_TLS(s)) - smtp_reply(s, "250-STARTTLS"); - if (ADVERTISE_AUTH(s)) - smtp_reply(s, "250-AUTH PLAIN LOGIN"); - smtp_reply(s, "250 HELP"); -} - -static void -smtp_proceed_auth(struct smtp_session *s, const char *args) -{ - char tmp[SMTP_LINE_MAX]; - char *eom, *method; - - (void)strlcpy(tmp, args, sizeof tmp); - - method = tmp; - eom = strchr(tmp, ' '); - if (eom == NULL) - eom = strchr(tmp, '\t'); - if (eom != NULL) - *eom++ = '\0'; - if (strcasecmp(method, "PLAIN") == 0) - smtp_rfc4954_auth_plain(s, eom); - else if (strcasecmp(method, "LOGIN") == 0) - smtp_rfc4954_auth_login(s, eom); - else - smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported", - esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED), - esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED), - method); -} - -static void -smtp_proceed_starttls(struct smtp_session *s, const char *args) -{ - smtp_reply(s, "220 %s Ready to start TLS", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); - smtp_enter_state(s, STATE_TLS); -} - -static void -smtp_proceed_mail_from(struct smtp_session *s, const char *args) -{ - char *copy; - char tmp[SMTP_LINE_MAX]; - - (void)strlcpy(tmp, args, sizeof tmp); - copy = tmp; - - if (!smtp_tx(s)) { - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_enter_state(s, STATE_QUIT); - return; - } - - if (smtp_mailaddr(&s->tx->evp.sender, copy, 1, ©, - s->smtpname) == 0) { - smtp_reply(s, "553 %s Sender address syntax error", - esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); - smtp_tx_free(s->tx); - return; - } - - smtp_tx_mail_from(s->tx, args); -} - -static void -smtp_proceed_rcpt_to(struct smtp_session *s, const char *args) -{ - smtp_tx_rcpt_to(s->tx, args); -} - -static void -smtp_proceed_data(struct smtp_session *s, const char *args) -{ - smtp_tx_open_message(s->tx); -} - -static void -smtp_proceed_quit(struct smtp_session *s, const char *args) -{ - smtp_reply(s, "221 %s Bye", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); - smtp_enter_state(s, STATE_QUIT); -} - -static void -smtp_proceed_noop(struct smtp_session *s, const char *args) -{ - smtp_reply(s, "250 %s Ok", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); -} - -static void -smtp_proceed_help(struct smtp_session *s, const char *args) -{ - const char *code = esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS); - - smtp_reply(s, "214-%s This is " SMTPD_NAME, code); - smtp_reply(s, "214-%s To report bugs in the implementation, " - "please contact bugs@openbsd.org", code); - smtp_reply(s, "214-%s with full details", code); - smtp_reply(s, "214 %s End of HELP info", code); -} - -static void -smtp_proceed_wiz(struct smtp_session *s, const char *args) -{ - smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), - esc_description(ESC_INVALID_COMMAND)); -} - -static void -smtp_proceed_commit(struct smtp_session *s, const char *args) -{ - smtp_message_end(s->tx); -} - -static void -smtp_proceed_rollback(struct smtp_session *s, const char *args) -{ - struct smtp_tx *tx; - - tx = s->tx; - - fclose(tx->ofile); - tx->ofile = NULL; - - smtp_tx_rollback(tx); - smtp_tx_free(tx); - smtp_enter_state(s, STATE_HELO); -} - -static void -smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg) -{ - char buf[1024], *user, *pass; - int len; - - switch (s->state) { - case STATE_HELO: - if (arg == NULL) { - smtp_enter_state(s, STATE_AUTH_INIT); - smtp_reply(s, "334 "); - return; - } - smtp_enter_state(s, STATE_AUTH_INIT); - /* FALLTHROUGH */ - - case STATE_AUTH_INIT: - /* String is not NUL terminated, leave room. */ - if ((len = base64_decode(arg, (unsigned char *)buf, - sizeof(buf) - 1)) == -1) - goto abort; - /* buf is a byte string, NUL terminate. */ - buf[len] = '\0'; - - /* - * Skip "foo" in "foo\0user\0pass", if present. - */ - user = memchr(buf, '\0', len); - if (user == NULL || user >= buf + len - 2) - goto abort; - user++; /* skip NUL */ - if (strlcpy(s->username, user, sizeof(s->username)) - >= sizeof(s->username)) - goto abort; - - pass = memchr(user, '\0', len - (user - buf)); - if (pass == NULL || pass >= buf + len - 2) - goto abort; - pass++; /* skip NUL */ - - m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->listener->authtable); - m_add_string(p_lka, user); - m_add_string(p_lka, pass); - m_close(p_lka); - tree_xset(&wait_parent_auth, s->id, s); - return; - - default: - fatal("smtp_rfc4954_auth_plain: unknown state"); - } - -abort: - smtp_reply(s, "501 %s %s: Syntax error", - esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), - esc_description(ESC_SYNTAX_ERROR)); - smtp_enter_state(s, STATE_HELO); -} - -static void -smtp_rfc4954_auth_login(struct smtp_session *s, char *arg) -{ - char buf[LINE_MAX]; - - switch (s->state) { - case STATE_HELO: - smtp_enter_state(s, STATE_AUTH_USERNAME); - if (arg != NULL && *arg != '\0') { - smtp_rfc4954_auth_login(s, arg); - return; - } - smtp_reply(s, "334 VXNlcm5hbWU6"); - return; - - case STATE_AUTH_USERNAME: - memset(s->username, 0, sizeof(s->username)); - if (base64_decode(arg, (unsigned char *)s->username, - sizeof(s->username) - 1) == -1) - goto abort; - - smtp_enter_state(s, STATE_AUTH_PASSWORD); - smtp_reply(s, "334 UGFzc3dvcmQ6"); - return; - - case STATE_AUTH_PASSWORD: - memset(buf, 0, sizeof(buf)); - if (base64_decode(arg, (unsigned char *)buf, - sizeof(buf)-1) == -1) - goto abort; - - m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->listener->authtable); - m_add_string(p_lka, s->username); - m_add_string(p_lka, buf); - m_close(p_lka); - tree_xset(&wait_parent_auth, s->id, s); - return; - - default: - fatal("smtp_rfc4954_auth_login: unknown state"); - } - -abort: - smtp_reply(s, "501 %s %s: Syntax error", - esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), - esc_description(ESC_SYNTAX_ERROR)); - smtp_enter_state(s, STATE_HELO); -} - -static void -smtp_lookup_servername(struct smtp_session *s) -{ - if (s->listener->hostnametable[0]) { - m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->listener->hostnametable); - m_add_sockaddr(p_lka, (struct sockaddr*)&s->listener->ss); - m_close(p_lka); - tree_xset(&wait_lka_helo, s->id, s); - return; - } - - smtp_connected(s); -} - -static void -smtp_connected(struct smtp_session *s) -{ - smtp_enter_state(s, STATE_CONNECTED); - - log_info("%016"PRIx64" smtp connected address=%s host=%s", - s->id, ss_to_text(&s->ss), s->rdns); - - smtp_filter_begin(s); - - smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss, - &s->listener->ss); - - smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss)); -} - -static void -smtp_proceed_connected(struct smtp_session *s) -{ - if (s->listener->flags & F_SMTPS) - smtp_cert_init(s); - else - smtp_send_banner(s); -} - -static void -smtp_send_banner(struct smtp_session *s) -{ - smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME); - s->banner_sent = 1; - smtp_report_link_greeting(s, s->smtpname); -} - -void -smtp_enter_state(struct smtp_session *s, int newstate) -{ - log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s, - smtp_strstate(s->state), - smtp_strstate(newstate)); - - s->state = newstate; -} - -static void -smtp_reply(struct smtp_session *s, char *fmt, ...) -{ - va_list ap; - int n; - char buf[LINE_MAX*2], tmp[LINE_MAX*2]; - - va_start(ap, fmt); - n = vsnprintf(buf, sizeof buf, fmt, ap); - va_end(ap); - if (n < 0) - fatalx("smtp_reply: response format error"); - if (n < 4) - fatalx("smtp_reply: response too short"); - if (n >= (int)sizeof buf) { - /* only first three bytes are used by SMTP logic, - * so if _our_ reply does not fit entirely in the - * buffer, it's ok to truncate. - */ - } - - log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf); - smtp_report_protocol_server(s, buf); - - switch (buf[0]) { - case '2': - if (s->tx) { - if (s->last_cmd == CMD_MAIL_FROM) { - smtp_report_tx_begin(s, s->tx->msgid); - smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1); - } - else if (s->last_cmd == CMD_RCPT_TO) - smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1); - } - break; - case '3': - if (s->tx) { - if (s->last_cmd == CMD_DATA) - smtp_report_tx_data(s, s->tx->msgid, 1); - } - break; - case '5': - case '4': - /* do not report smtp_tx_mail/smtp_tx_rcpt errors - * if they happened outside of a transaction. - */ - if (s->tx) { - if (s->last_cmd == CMD_MAIL_FROM) - smtp_report_tx_mail(s, s->tx->msgid, - s->cmd + 10, buf[0] == '4' ? -1 : 0); - else if (s->last_cmd == CMD_RCPT_TO) - smtp_report_tx_rcpt(s, - s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0); - else if (s->last_cmd == CMD_DATA && s->tx->rcptcount) - smtp_report_tx_data(s, s->tx->msgid, - buf[0] == '4' ? -1 : 0); - } - - if (s->flags & SF_BADINPUT) { - log_info("%016"PRIx64" smtp " - "bad-input result=\"%.*s\"", - s->id, n, buf); - } - else if (s->state == STATE_AUTH_INIT) { - log_info("%016"PRIx64" smtp " - "failed-command " - "command=\"AUTH PLAIN (...)\" result=\"%.*s\"", - s->id, n, buf); - } - else if (s->state == STATE_AUTH_USERNAME) { - log_info("%016"PRIx64" smtp " - "failed-command " - "command=\"AUTH LOGIN (username)\" result=\"%.*s\"", - s->id, n, buf); - } - else if (s->state == STATE_AUTH_PASSWORD) { - log_info("%016"PRIx64" smtp " - "failed-command " - "command=\"AUTH LOGIN (password)\" result=\"%.*s\"", - s->id, n, buf); - } - else { - strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE); - log_info("%016"PRIx64" smtp " - "failed-command command=\"%s\" " - "result=\"%.*s\"", - s->id, tmp, n, buf); - } - break; - } - - io_xprintf(s->io, "%s\r\n", buf); -} - -static void -smtp_free(struct smtp_session *s, const char * reason) -{ - if (s->tx) { - if (s->tx->msgid) - smtp_tx_rollback(s->tx); - smtp_tx_free(s->tx); - } - - smtp_report_link_disconnect(s); - smtp_filter_end(s); - - if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS) - stat_decrement("smtp.smtps", 1); - if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS) - stat_decrement("smtp.tls", 1); - - io_free(s->io); - free(s); - - smtp_collect(); -} - -static int -smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args, - const char *domain) -{ - char *p, *e; - - if (line == NULL) - return (0); - - if (*line != '<') - return (0); - - e = strchr(line, '>'); - if (e == NULL) - return (0); - *e++ = '\0'; - while (*e == ' ') - e++; - *args = e; - - if (!text_to_mailaddr(maddr, line + 1)) - return (0); - - p = strchr(maddr->user, ':'); - if (p != NULL) { - p++; - memmove(maddr->user, p, strlen(p) + 1); - } - - /* accept empty return-path in MAIL FROM, required for bounces */ - if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0') - return (1); - - /* no or invalid user-part, reject */ - if (maddr->user[0] == '\0' || !valid_localpart(maddr->user)) - return (0); - - /* no domain part, local user */ - if (maddr->domain[0] == '\0') { - (void)strlcpy(maddr->domain, domain, - sizeof(maddr->domain)); - } - - if (!valid_domainpart(maddr->domain)) - return (0); - - return (1); -} - -static void -smtp_cert_init(struct smtp_session *s) -{ - const char *name; - int fallback; - - if (s->listener->pki_name[0]) { - name = s->listener->pki_name; - fallback = 0; - } - else { - name = s->smtpname; - fallback = 1; - } - - if (cert_init(name, fallback, smtp_cert_init_cb, s)) - tree_xset(&wait_ssl_init, s->id, s); -} - -static void -smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert, - size_t cert_len) -{ - struct smtp_session *s = arg; - void *ssl, *ssl_ctx; - - tree_pop(&wait_ssl_init, s->id); - - if (status == CA_FAIL) { - log_info("%016"PRIx64" smtp disconnected " - "reason=ca-failure", - s->id); - smtp_free(s, "CA failure"); - return; - } - - ssl_ctx = dict_get(env->sc_ssl_dict, name); - ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY); - io_set_read(s->io); - io_start_tls(s->io, ssl); -} - -static void -smtp_cert_verify(struct smtp_session *s) -{ - const char *name; - int fallback; - - if (s->listener->ca_name[0]) { - name = s->listener->ca_name; - fallback = 0; - } - else { - name = s->smtpname; - fallback = 1; - } - - if (cert_verify(io_tls(s->io), name, fallback, smtp_cert_verify_cb, s)) { - tree_xset(&wait_ssl_verify, s->id, s); - io_pause(s->io, IO_IN); - } -} - -static void -smtp_cert_verify_cb(void *arg, int status) -{ - struct smtp_session *s = arg; - const char *reason = NULL; - int resume; - - resume = tree_pop(&wait_ssl_verify, s->id) != NULL; - - switch (status) { - case CERT_OK: - reason = "cert-ok"; - s->flags |= SF_VERIFIED; - break; - case CERT_NOCA: - reason = "no-ca"; - break; - case CERT_NOCERT: - reason = "no-client-cert"; - break; - case CERT_INVALID: - reason = "cert-invalid"; - break; - default: - reason = "cert-check-failed"; - break; - } - - log_debug("smtp: %p: smtp_cert_verify_cb: %s", s, reason); - - if (!(s->flags & SF_VERIFIED) && (s->listener->flags & F_TLS_VERIFY)) { - log_info("%016"PRIx64" smtp disconnected " - " reason=%s", s->id, - reason); - smtp_free(s, "SSL certificate check failed"); - return; - } - - smtp_tls_verified(s); - if (resume) - io_resume(s->io, IO_IN); -} - -static void -smtp_auth_failure_resume(int fd, short event, void *p) -{ - struct smtp_session *s = p; - - smtp_reply(s, "535 Authentication failed"); - smtp_enter_state(s, STATE_HELO); -} - -static void -smtp_auth_failure_pause(struct smtp_session *s) -{ - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = arc4random_uniform(1000000); - log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, " - "will defer answer for %lu microseconds", tv.tv_usec); - evtimer_set(&s->pause, smtp_auth_failure_resume, s); - evtimer_add(&s->pause, &tv); -} - -static int -smtp_tx(struct smtp_session *s) -{ - struct smtp_tx *tx; - - tx = calloc(1, sizeof(*tx)); - if (tx == NULL) - return 0; - - TAILQ_INIT(&tx->rcpts); - - s->tx = tx; - tx->session = s; - - /* setup the envelope */ - tx->evp.ss = s->ss; - (void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag)); - (void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname)); - (void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname); - (void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo)); - (void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username)); - - if (s->flags & SF_BOUNCE) - tx->evp.flags |= EF_BOUNCE; - if (s->flags & SF_AUTHENTICATED) - tx->evp.flags |= EF_AUTHENTICATED; - - if ((tx->parser = rfc5322_parser_new()) == NULL) { - free(tx); - return 0; - } - - return 1; -} - -static void -smtp_tx_free(struct smtp_tx *tx) -{ - struct smtp_rcpt *rcpt; - - rfc5322_free(tx->parser); - - while ((rcpt = TAILQ_FIRST(&tx->rcpts))) { - TAILQ_REMOVE(&tx->rcpts, rcpt, entry); - free(rcpt); - } - - if (tx->ofile) - fclose(tx->ofile); - - tx->session->tx = NULL; - - free(tx); -} - -static void -smtp_tx_mail_from(struct smtp_tx *tx, const char *line) -{ - char *opt; - char *copy; - char tmp[SMTP_LINE_MAX]; - - (void)strlcpy(tmp, line, sizeof tmp); - copy = tmp; - - if (smtp_mailaddr(&tx->evp.sender, copy, 1, ©, - tx->session->smtpname) == 0) { - smtp_reply(tx->session, "553 %s Sender address syntax error", - esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); - smtp_tx_free(tx); - return; - } - - while ((opt = strsep(©, " "))) { - if (*opt == '\0') - continue; - - if (strncasecmp(opt, "AUTH=", 5) == 0) - log_debug("debug: smtp: AUTH in MAIL FROM command"); - else if (strncasecmp(opt, "SIZE=", 5) == 0) - log_debug("debug: smtp: SIZE in MAIL FROM command"); - else if (strcasecmp(opt, "BODY=7BIT") == 0) - /* XXX only for this transaction */ - tx->session->flags &= ~SF_8BITMIME; - else if (strcasecmp(opt, "BODY=8BITMIME") == 0) - ; - else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "RET=", 4) == 0) { - opt += 4; - if (strcasecmp(opt, "HDRS") == 0) - tx->evp.dsn_ret = DSN_RETHDRS; - else if (strcasecmp(opt, "FULL") == 0) - tx->evp.dsn_ret = DSN_RETFULL; - } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ENVID=", 6) == 0) { - opt += 6; - if (strlcpy(tx->evp.dsn_envid, opt, sizeof(tx->evp.dsn_envid)) - >= sizeof(tx->evp.dsn_envid)) { - smtp_reply(tx->session, - "503 %s %s: option too large, truncated: %s", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt); - smtp_tx_free(tx); - return; - } - } else { - smtp_reply(tx->session, "503 %s %s: Unsupported option %s", - esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), - esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt); - smtp_tx_free(tx); - return; - } - } - - /* only check sendertable if defined and user has authenticated */ - if (tx->session->flags & SF_AUTHENTICATED && - tx->session->listener->sendertable[0]) { - m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1); - m_add_id(p_lka, tx->session->id); - m_add_string(p_lka, tx->session->listener->sendertable); - m_add_string(p_lka, tx->session->username); - m_add_mailaddr(p_lka, &tx->evp.sender); - m_close(p_lka); - tree_xset(&wait_lka_mail, tx->session->id, tx->session); - } - else - smtp_tx_create_message(tx); -} - -static void -smtp_tx_create_message(struct smtp_tx *tx) -{ - m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1); - m_add_id(p_queue, tx->session->id); - m_close(p_queue); - tree_xset(&wait_queue_msg, tx->session->id, tx->session); -} - -static void -smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line) -{ - char *opt, *p; - char *copy; - char tmp[SMTP_LINE_MAX]; - - (void)strlcpy(tmp, line, sizeof tmp); - copy = tmp; - - if (tx->rcptcount >= env->sc_session_max_rcpt) { - smtp_reply(tx->session, "451 %s %s: Too many recipients", - esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), - esc_description(ESC_TOO_MANY_RECIPIENTS)); - return; - } - - if (smtp_mailaddr(&tx->evp.rcpt, copy, 0, ©, - tx->session->smtpname) == 0) { - smtp_reply(tx->session, - "501 %s Recipient address syntax error", - esc_code(ESC_STATUS_PERMFAIL, - ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX)); - return; - } - - while ((opt = strsep(©, " "))) { - if (*opt == '\0') - continue; - - if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "NOTIFY=", 7) == 0) { - opt += 7; - while ((p = strsep(&opt, ","))) { - if (strcasecmp(p, "SUCCESS") == 0) - tx->evp.dsn_notify |= DSN_SUCCESS; - else if (strcasecmp(p, "FAILURE") == 0) - tx->evp.dsn_notify |= DSN_FAILURE; - else if (strcasecmp(p, "DELAY") == 0) - tx->evp.dsn_notify |= DSN_DELAY; - else if (strcasecmp(p, "NEVER") == 0) - tx->evp.dsn_notify |= DSN_NEVER; - } - - if (tx->evp.dsn_notify & DSN_NEVER && - tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE | - DSN_DELAY)) { - smtp_reply(tx->session, - "553 NOTIFY option NEVER cannot be" - " combined with other options"); - return; - } - } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ORCPT=", 6) == 0) { - opt += 6; - - if (strncasecmp(opt, "rfc822;", 7) == 0) - opt += 7; - - if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) || - !valid_localpart(tx->evp.dsn_orcpt.user) || - !valid_domainpart(tx->evp.dsn_orcpt.domain)) { - smtp_reply(tx->session, - "553 ORCPT address syntax error"); - return; - } - } else { - smtp_reply(tx->session, "503 Unsupported option %s", opt); - return; - } - } - - m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1); - m_add_id(p_lka, tx->session->id); - m_add_envelope(p_lka, &tx->evp); - m_close(p_lka); - tree_xset(&wait_lka_rcpt, tx->session->id, tx->session); -} - -static void -smtp_tx_open_message(struct smtp_tx *tx) -{ - m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1); - m_add_id(p_queue, tx->session->id); - m_add_msgid(p_queue, tx->msgid); - m_close(p_queue); - tree_xset(&wait_queue_fd, tx->session->id, tx->session); -} - -static void -smtp_tx_commit(struct smtp_tx *tx) -{ - m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1); - m_add_id(p_queue, tx->session->id); - m_add_msgid(p_queue, tx->msgid); - m_close(p_queue); - tree_xset(&wait_queue_commit, tx->session->id, tx->session); - smtp_filter_data_end(tx->session); -} - -static void -smtp_tx_rollback(struct smtp_tx *tx) -{ - m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1); - m_add_msgid(p_queue, tx->msgid); - m_close(p_queue); - smtp_report_tx_rollback(tx->session, tx->msgid); - smtp_report_tx_reset(tx->session, tx->msgid); - smtp_filter_data_end(tx->session); -} - -static int -smtp_tx_dataline(struct smtp_tx *tx, const char *line) -{ - struct rfc5322_result res; - int r; - - log_trace(TRACE_SMTP, "<<< [MSG] %s", line); - - if (!strcmp(line, ".")) { - smtp_report_protocol_client(tx->session, "."); - log_trace(TRACE_SMTP, "<<< [EOM]"); - if (tx->error) - return 1; - line = NULL; - } - else { - /* ignore data line if an error is set */ - if (tx->error) - return 0; - - /* escape lines starting with a '.' */ - if (line[0] == '.') - line += 1; - } - - if (rfc5322_push(tx->parser, line) == -1) { - log_warnx("failed to push dataline"); - tx->error = TX_ERROR_INTERNAL; - return 0; - } - - for(;;) { - r = rfc5322_next(tx->parser, &res); - switch (r) { - case -1: - if (errno == ENOMEM) - tx->error = TX_ERROR_INTERNAL; - else - tx->error = TX_ERROR_MALFORMED; - return 0; - - case RFC5322_NONE: - /* Need more data */ - return 0; - - case RFC5322_HEADER_START: - /* ignore bcc */ - if (!strcasecmp("Bcc", res.hdr)) - continue; - - if (!strcasecmp("To", res.hdr) || - !strcasecmp("Cc", res.hdr) || - !strcasecmp("From", res.hdr)) { - rfc5322_unfold_header(tx->parser); - continue; - } - - if (!strcasecmp("Received", res.hdr)) { - if (++tx->rcvcount >= MAX_HOPS_COUNT) { - log_warnx("warn: loop detected"); - tx->error = TX_ERROR_LOOP; - return 0; - } - } - else if (!tx->has_date && !strcasecmp("Date", res.hdr)) - tx->has_date = 1; - else if (!tx->has_message_id && - !strcasecmp("Message-Id", res.hdr)) - tx->has_message_id = 1; - - smtp_message_printf(tx, "%s:%s\n", res.hdr, res.value); - break; - - case RFC5322_HEADER_CONT: - - if (!strcasecmp("Bcc", res.hdr) || - !strcasecmp("To", res.hdr) || - !strcasecmp("Cc", res.hdr) || - !strcasecmp("From", res.hdr)) - continue; - - smtp_message_printf(tx, "%s\n", res.value); - break; - - case RFC5322_HEADER_END: - if (!strcasecmp("To", res.hdr) || - !strcasecmp("Cc", res.hdr) || - !strcasecmp("From", res.hdr)) - header_domain_append_callback(tx, res.hdr, - res.value); - break; - - case RFC5322_END_OF_HEADERS: - if (tx->session->listener->local || - tx->session->listener->port == 587) { - - if (!tx->has_date) { - log_debug("debug: %p: adding Date", tx); - smtp_message_printf(tx, "Date: %s\n", - time_to_text(tx->time)); - } - - if (!tx->has_message_id) { - log_debug("debug: %p: adding Message-ID", tx); - smtp_message_printf(tx, - "Message-ID: <%016"PRIx64"@%s>\n", - generate_uid(), - tx->session->listener->hostname); - } - } - break; - - case RFC5322_BODY_START: - case RFC5322_BODY: - smtp_message_printf(tx, "%s\n", res.value); - break; - - case RFC5322_END_OF_MESSAGE: - return 1; - - default: - fatalx("%s", __func__); - } - } -} - -static int -smtp_tx_filtered_dataline(struct smtp_tx *tx, const char *line) -{ - if (!strcmp(line, ".")) - line = NULL; - else { - /* ignore data line if an error is set */ - if (tx->error) - return 0; - } - io_printf(tx->filter, "%s\n", line ? line : "."); - return line ? 0 : 1; -} - -static void -smtp_tx_eom(struct smtp_tx *tx) -{ - smtp_filter_phase(FILTER_COMMIT, tx->session, NULL); -} - -static int -smtp_message_fd(struct smtp_tx *tx, int fd) -{ - struct smtp_session *s; - - s = tx->session; - - log_debug("smtp: %p: message fd %d", s, fd); - - if ((tx->ofile = fdopen(fd, "w")) == NULL) { - close(fd); - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - smtp_enter_state(s, STATE_QUIT); - return 0; - } - return 1; -} - -static void -filter_session_io(struct io *io, int evt, void *arg) -{ - struct smtp_tx*tx = arg; - char*line = NULL; - ssize_t len; - - log_trace(TRACE_IO, "filter session io (smtp): %p: %s %s", tx, io_strevent(evt), - io_strio(io)); - - switch (evt) { - case IO_DATAIN: - nextline: - line = io_getline(tx->filter, &len); - /* No complete line received */ - if (line == NULL) - return; - - if (smtp_tx_dataline(tx, line)) { - smtp_tx_eom(tx); - return; - } - - goto nextline; - } -} - -static void -smtp_filter_fd(struct smtp_tx *tx, int fd) -{ - struct smtp_session *s; - - s = tx->session; - - log_debug("smtp: %p: filter fd %d", s, fd); - - tx->filter = io_new(); - io_set_fd(tx->filter, fd); - io_set_callback(tx->filter, filter_session_io, tx); -} - -static void -smtp_message_begin(struct smtp_tx *tx) -{ - struct smtp_session *s; - X509 *x; - int (*m_printf)(struct smtp_tx *, const char *, ...); - - m_printf = smtp_message_printf; - if (tx->filter) - m_printf = smtp_filter_printf; - - s = tx->session; - - log_debug("smtp: %p: message begin", s); - - smtp_reply(s, "354 Enter mail, end with \".\"" - " on a line by itself"); - - if (s->junk || (s->tx && s->tx->junk)) - m_printf(tx, "X-Spam: Yes\n"); - - m_printf(tx, "Received: "); - if (!(s->listener->flags & F_MASK_SOURCE)) { - m_printf(tx, "from %s (%s %s%s%s)", - s->helo, - s->rdns, - s->ss.ss_family == AF_INET6 ? "" : "[", - ss_to_text(&s->ss), - s->ss.ss_family == AF_INET6 ? "" : "]"); - } - m_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x", - s->smtpname, - SMTPD_NAME, - s->flags & SF_EHLO ? "E" : "", - s->flags & SF_SECURE ? "S" : "", - s->flags & SF_AUTHENTICATED ? "A" : "", - tx->msgid); - - if (s->flags & SF_SECURE) { - x = SSL_get_peer_certificate(io_tls(s->io)); - m_printf(tx, " (%s:%s:%d:%s)", - SSL_get_version(io_tls(s->io)), - SSL_get_cipher_name(io_tls(s->io)), - SSL_get_cipher_bits(io_tls(s->io), NULL), - (s->flags & SF_VERIFIED) ? "YES" : (x ? "FAIL" : "NO")); - X509_free(x); - - if (s->listener->flags & F_RECEIVEDAUTH) { - m_printf(tx, " auth=%s", - s->username[0] ? "yes" : "no"); - if (s->username[0]) - m_printf(tx, " user=%s", s->username); - } - } - - if (tx->rcptcount == 1) { - m_printf(tx, "\n\tfor <%s@%s>", - tx->evp.rcpt.user, - tx->evp.rcpt.domain); - } - - m_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time))); - - smtp_enter_state(s, STATE_BODY); -} - -static void -smtp_message_end(struct smtp_tx *tx) -{ - struct smtp_session *s; - - s = tx->session; - - log_debug("debug: %p: end of message, error=%d", s, tx->error); - - fclose(tx->ofile); - tx->ofile = NULL; - - switch(tx->error) { - case TX_OK: - smtp_tx_commit(tx); - return; - - case TX_ERROR_SIZE: - smtp_reply(s, "554 %s %s: Transaction failed, message too big", - esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM), - esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM)); - break; - - case TX_ERROR_LOOP: - smtp_reply(s, "500 %s %s: Loop detected", - esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED), - esc_description(ESC_ROUTING_LOOP_DETECTED)); - break; - - case TX_ERROR_MALFORMED: - smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant", - esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED), - esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED)); - break; - - case TX_ERROR_IO: - case TX_ERROR_RESOURCES: - smtp_reply(s, "421 %s Temporary Error", - esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); - break; - - default: - /* fatal? */ - smtp_reply(s, "421 Internal server error"); - } - - smtp_tx_rollback(tx); - smtp_tx_free(tx); - smtp_enter_state(s, STATE_HELO); -} - -static int -smtp_filter_printf(struct smtp_tx *tx, const char *fmt, ...) -{ - va_list ap; - int len; - - if (tx->error) - return -1; - - va_start(ap, fmt); - len = io_vprintf(tx->filter, fmt, ap); - va_end(ap); - - if (len < 0) { - log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id); - tx->error = TX_ERROR_IO; - } - else - tx->odatalen += len; - - return len; -} - -static int -smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...) -{ - va_list ap; - int len; - - if (tx->error) - return -1; - - va_start(ap, fmt); - len = vfprintf(tx->ofile, fmt, ap); - va_end(ap); - - if (len == -1) { - log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id); - tx->error = TX_ERROR_IO; - } - else - tx->odatalen += len; - - return len; -} - -#define CASE(x) case x : return #x - -const char * -smtp_strstate(int state) -{ - static char buf[32]; - - switch (state) { - CASE(STATE_NEW); - CASE(STATE_CONNECTED); - CASE(STATE_TLS); - CASE(STATE_HELO); - CASE(STATE_AUTH_INIT); - CASE(STATE_AUTH_USERNAME); - CASE(STATE_AUTH_PASSWORD); - CASE(STATE_AUTH_FINALIZE); - CASE(STATE_BODY); - CASE(STATE_QUIT); - default: - (void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state); - return (buf); - } -} - - -static void -smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns, - const struct sockaddr_storage *ss_src, - const struct sockaddr_storage *ss_dest) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest); -} - -static void -smtp_report_link_greeting(struct smtp_session *s, - const char *domain) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_greeting("smtp-in", s->id, domain); -} - -static void -smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_identify("smtp-in", s->id, method, identity); -} - -static void -smtp_report_link_tls(struct smtp_session *s, const char *ssl) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_tls("smtp-in", s->id, ssl); -} - -static void -smtp_report_link_disconnect(struct smtp_session *s) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_disconnect("smtp-in", s->id); -} - -static void -smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_auth("smtp-in", s->id, user, result); -} - -static void -smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_reset("smtp-in", s->id, msgid); -} - -static void -smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_begin("smtp-in", s->id, msgid); -} - -static void -smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok) -{ - char mailaddr[SMTPD_MAXMAILADDRSIZE]; - char *p; - - if (! SESSION_FILTERED(s)) - return; - - if ((p = strchr(address, '<')) == NULL) - return; - (void)strlcpy(mailaddr, p + 1, sizeof mailaddr); - if ((p = strchr(mailaddr, '>')) == NULL) - return; - *p = '\0'; - - report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok); -} - -static void -smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok) -{ - char mailaddr[SMTPD_MAXMAILADDRSIZE]; - char *p; - - if (! SESSION_FILTERED(s)) - return; - - if ((p = strchr(address, '<')) == NULL) - return; - (void)strlcpy(mailaddr, p + 1, sizeof mailaddr); - if ((p = strchr(mailaddr, '>')) == NULL) - return; - *p = '\0'; - - report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok); -} - -static void -smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid); -} - -static void -smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_data("smtp-in", s->id, msgid, ok); -} - -static void -smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz); -} - -static void -smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_rollback("smtp-in", s->id, msgid); -} - -static void -smtp_report_protocol_client(struct smtp_session *s, const char *command) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_protocol_client("smtp-in", s->id, command); -} - -static void -smtp_report_protocol_server(struct smtp_session *s, const char *response) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_protocol_server("smtp-in", s->id, response); -} - -static void -smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_filter_response("smtp-in", s->id, phase, response, param); -} - -static void -smtp_report_timeout(struct smtp_session *s) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_timeout("smtp-in", s->id); -} diff --git a/smtpd/smtpc.c b/smtpd/smtpc.c deleted file mode 100644 index 59479703..00000000 --- a/smtpd/smtpc.c +++ /dev/null @@ -1,465 +0,0 @@ -/* $OpenBSD: smtpc.c,v 1.10 2019/09/21 09:04:08 semarie Exp $ */ - -/* - * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> - -#include <event.h> -#include <limits.h> -#include <netdb.h> -#include <pwd.h> -#include <resolv.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <unistd.h> - -#include <openssl/ssl.h> - -#include "smtp.h" -#include "ssl.h" -#include "log.h" - -static void parse_server(char *); -static void parse_message(FILE *); -static void resume(void); - -static int verbose = 1; -static int done = 0; -static int noaction = 0; -static struct addrinfo *res0, *ai; -static struct smtp_params params; -static struct smtp_mail mail; -static const char *servname = NULL; - -static SSL_CTX *ssl_ctx; - -static void -usage(void) -{ - extern char *__progname; - - fprintf(stderr, - "usage: %s [-Chnv] [-F from] [-H helo] [-s server] [-S name] rcpt ...\n", - __progname); - exit(1); -} - -int -main(int argc, char **argv) -{ - char hostname[256]; - int ch, i; - char *server = "localhost"; - struct passwd *pw; - - log_init(1, 0); - - if (gethostname(hostname, sizeof(hostname)) == -1) - fatal("gethostname"); - - if ((pw = getpwuid(getuid())) == NULL) - fatal("getpwuid"); - - memset(¶ms, 0, sizeof(params)); - - params.linemax = 16392; - params.ibufmax = 65536; - params.obufmax = 65536; - params.timeout = 100000; - params.helo = hostname; - - params.tls_verify = 1; - - memset(&mail, 0, sizeof(mail)); - mail.from = pw->pw_name; - - while ((ch = getopt(argc, argv, "CF:H:S:hns:v")) != -1) { - switch (ch) { - case 'C': - params.tls_verify = 0; - break; - case 'F': - mail.from = optarg; - break; - case 'H': - params.helo = optarg; - break; - case 'S': - servname = optarg; - break; - case 'h': - usage(); - break; - case 'n': - noaction = 1; - break; - case 's': - server = optarg; - break; - case 'v': - verbose++; - break; - default: - usage(); - } - } - - log_setverbose(verbose); - - argc -= optind; - argv += optind; - - if (argc) { - mail.rcpt = calloc(argc, sizeof(*mail.rcpt)); - if (mail.rcpt == NULL) - fatal("calloc"); - for (i = 0; i < argc; i++) - mail.rcpt[i].to = argv[i]; - mail.rcptcount = argc; - } - - ssl_init(); - event_init(); - - ssl_ctx = ssl_ctx_create(NULL, NULL, 0, NULL); - if (!SSL_CTX_load_verify_locations(ssl_ctx, - X509_get_default_cert_file(), NULL)) - fatal("SSL_CTX_load_verify_locations"); - if (!SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_client_method())) - fatal("SSL_CTX_set_ssl_version"); - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE , NULL); - -#if HAVE_PLEDGE - if (pledge("stdio inet dns tmppath", NULL) == -1) - fatal("pledge"); -#endif - - if (!noaction) - parse_message(stdin); - -#if HAVE_PLEDGE - if (pledge("stdio inet dns", NULL) == -1) - fatal("pledge"); -#endif - - parse_server(server); - -#if HAVE_PLEDGE - if (pledge("stdio inet", NULL) == -1) - fatal("pledge"); -#endif - - resume(); - - log_debug("done..."); - - return 0; -} - -static void -parse_server(char *server) -{ - struct addrinfo hints; - char *scheme, *creds, *host, *port, *p, *c; - int error; - - creds = NULL; - host = NULL; - port = NULL; - scheme = server; - - p = strstr(server, "://"); - if (p) { - *p = '\0'; - p += 3; - /* check for credentials */ - c = strchr(p, '@'); - if (c) { - creds = p; - *c = '\0'; - host = c + 1; - } else - host = p; - } else { - /* Assume a simple server name */ - scheme = "smtp"; - host = server; - } - - if (host[0] == '[') { - /* IPV6 address? */ - p = strchr(host, ']'); - if (p) { - if (p[1] == ':' || p[1] == '\0') { - *p++ = '\0'; /* remove ']' */ - host++; /* skip '[' */ - if (*p == ':') - port = p + 1; - } - } - } - else { - port = strchr(host, ':'); - if (port) - *port++ = '\0'; - } - - if (port && port[0] == '\0') - port = NULL; - - if (creds) { - p = strchr(creds, ':'); - if (p == NULL) - fatalx("invalid credentials"); - *p = '\0'; - - params.auth_user = creds; - params.auth_pass = p + 1; - } - params.tls_req = TLS_YES; - - if (!strcmp(scheme, "lmtp")) { - params.lmtp = 1; - } - else if (!strcmp(scheme, "lmtp+tls")) { - params.lmtp = 1; - params.tls_req = TLS_FORCE; - } - else if (!strcmp(scheme, "lmtp+notls")) { - params.lmtp = 1; - params.tls_req = TLS_NO; - } - else if (!strcmp(scheme, "smtps")) { - params.tls_req = TLS_SMTPS; - if (port == NULL) - port = "smtps"; - } - else if (!strcmp(scheme, "smtp")) { - } - else if (!strcmp(scheme, "smtp+tls")) { - params.tls_req = TLS_FORCE; - } - else if (!strcmp(scheme, "smtp+notls")) { - params.tls_req = TLS_NO; - } - else - fatalx("invalid url scheme %s", scheme); - - if (port == NULL) - port = "smtp"; - - if (servname == NULL) - servname = host; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(host, port, &hints, &res0); - if (error) - fatalx("%s: %s", host, gai_strerror(error)); - ai = res0; -} - -void -parse_message(FILE *ifp) -{ - char *line = NULL; - size_t linesz = 0; - ssize_t len; - - if ((mail.fp = tmpfile()) == NULL) - fatal("tmpfile"); - - for (;;) { - if ((len = getline(&line, &linesz, ifp)) == -1) { - if (feof(ifp)) - break; - fatal("getline"); - } - - if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n') - line[--len - 1] = '\n'; - - if (fwrite(line, 1, len, mail.fp) != (size_t)len) - fatal("fwrite"); - - if (line[len - 1] != '\n' && fputc('\n', mail.fp) == EOF) - fatal("fputc"); - } - - fclose(ifp); - rewind(mail.fp); -} - -void -resume(void) -{ - static int started = 0; - char host[256]; - char serv[16]; - - if (done) { - event_loopexit(NULL); - return; - } - - if (ai == NULL) - fatalx("no more host"); - - getnameinfo(ai->ai_addr, SA_LEN(ai->ai_addr), - host, sizeof(host), serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV); - log_debug("trying host %s port %s...", host, serv); - - params.dst = ai->ai_addr; - if (smtp_connect(¶ms, NULL) == NULL) - fatal("smtp_connect"); - - if (started == 0) { - started = 1; - event_loop(0); - } -} - -void -log_trace(int lvl, const char *emsg, ...) -{ - va_list ap; - - if (verbose > lvl) { - va_start(ap, emsg); - vlog(LOG_DEBUG, emsg, ap); - va_end(ap); - } -} - -void -smtp_verify_server_cert(void *tag, struct smtp_client *proto, void *ctx) -{ - SSL *ssl = ctx; - X509 *cert; - long res; - int match; - - if ((cert = SSL_get_peer_certificate(ssl))) { - (void)ssl_check_name(cert, servname, &match); - X509_free(cert); - res = SSL_get_verify_result(ssl); - if (res == X509_V_OK) { - if (match) { - log_debug("valid certificate"); - smtp_cert_verified(proto, CERT_OK); - } - else { - log_debug("certificate does not match hostname"); - smtp_cert_verified(proto, CERT_INVALID); - } - return; - } - log_debug("certificate validation error %ld", res); - } - else - log_debug("no certificate provided"); - - smtp_cert_verified(proto, CERT_INVALID); -} - -void -smtp_require_tls(void *tag, struct smtp_client *proto) -{ - SSL *ssl = NULL; - - if ((ssl = SSL_new(ssl_ctx)) == NULL) - fatal("SSL_new"); - smtp_set_tls(proto, ssl); -} - -void -smtp_ready(void *tag, struct smtp_client *proto) -{ - log_debug("connection ready..."); - - if (done || noaction) - smtp_quit(proto); - else - smtp_sendmail(proto, &mail); -} - -void -smtp_failed(void *tag, struct smtp_client *proto, int failure, const char *detail) -{ - switch (failure) { - case FAIL_INTERNAL: - log_warnx("internal error: %s", detail); - break; - case FAIL_CONN: - log_warnx("connection error: %s", detail); - break; - case FAIL_PROTO: - log_warnx("protocol error: %s", detail); - break; - case FAIL_IMPL: - log_warnx("missing feature: %s", detail); - break; - case FAIL_RESP: - log_warnx("rejected by server: %s", detail); - break; - default: - fatalx("unknown failure %d: %s", failure, detail); - } -} - -void -smtp_status(void *tag, struct smtp_client *proto, struct smtp_status *status) -{ - log_info("%s: %s: %s", status->rcpt->to, status->cmd, status->status); -} - -void -smtp_done(void *tag, struct smtp_client *proto, struct smtp_mail *mail) -{ - int i; - - log_debug("mail done..."); - - if (noaction) - return; - - for (i = 0; i < mail->rcptcount; i++) - if (!mail->rcpt[i].done) - return; - - done = 1; -} - -void -smtp_closed(void *tag, struct smtp_client *proto) -{ - log_debug("connection closed..."); - - ai = ai->ai_next; - if (noaction && ai == NULL) - done = 1; - - resume(); -} diff --git a/smtpd/smtpctl.8 b/smtpd/smtpctl.8 deleted file mode 100644 index 1efcff63..00000000 --- a/smtpd/smtpctl.8 +++ /dev/null @@ -1,336 +0,0 @@ -.\" $OpenBSD: smtpctl.8,v 1.64 2018/09/18 06:21:45 miko Exp $ -.\" -.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> -.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: September 18 2018 $ -.Dt SMTPCTL 8 -.Os -.Sh NAME -.Nm smtpctl , -.Nm mailq -.Nd control the Simple Mail Transfer Protocol daemon -.Sh SYNOPSIS -.Nm -.Ar command -.Op Ar argument ... -.Nm mailq -.Sh DESCRIPTION -The -.Nm -program controls -.Xr smtpd 8 . -Commands may be abbreviated to the minimum unambiguous prefix; for example, -.Cm sh ro -for -.Cm show routes . -.Pp -The -.Nm mailq -command is provided for compatibility with other MTAs -and is simply a shortcut for -.Cm show queue . -.Pp -The following commands are available: -.Bl -tag -width Ds -.It Cm discover Ar envelope-id | message-id -Schedule a single envelope, or all envelopes with the same message ID -that were manually moved to the queue. -.It Cm encrypt Op Ar string -Encrypt the password -.Ar string -to a representation suitable for user credentials and print it to the -standard output. -If -.Ar string -is not provided, cleartext passwords are read from standard input. -.Pp -It is advised to avoid providing the password as a parameter as it will be -visible from -.Xr top 1 -and -.Xr ps 1 -output. -.It Cm log brief -Disable verbose debug logging. -.It Cm log verbose -Enable verbose debug logging. -.It Cm monitor -Display updates of some -.Xr smtpd 8 -internal counters in one second intervals. -Each line reports the increment of all counters since the last update, -except for some counters which are always absolute values. -The first line reports the current value of each counter. -The fields are: -.Pp -.Bl -bullet -compact -.It -Current number of active SMTP clients (absolute value). -.It -New SMTP clients. -.It -Disconnected clients. -.It -Current number of envelopes in the queue (absolute value). -.It -Newly enqueued envelopes. -.It -Dequeued envelopes. -.It -Successful deliveries. -.It -Temporary failures. -.It -Permanent failures. -.It -Message loops. -.It -Expired envelopes. -.It -Envelopes removed by the administrator. -.It -Generated bounces. -.El -.It Cm pause envelope Ar envelope-id | message-id | Cm all -Temporarily suspend scheduling for the envelope with the given ID, -envelopes with the given message ID, -or all envelopes. -.It Cm pause mda -Temporarily stop deliveries to local users. -.It Cm pause mta -Temporarily stop relaying and deliveries to -remote users. -.It Cm pause smtp -Temporarily stop accepting incoming sessions. -.It Cm profile Ar subsystem -Enables real-time profiling of -.Ar subsystem . -Supported subsystems are: -.Pp -.Bl -bullet -compact -.It -queue, to profile cost of queue IO -.It -imsg, to profile cost of event handlers -.El -.It Cm remove Ar envelope-id | message-id | Cm all -Remove a single envelope, -envelopes with the given message ID, -or all envelopes. -.It Cm resume envelope Ar envelope-id | message-id | Cm all -Resume scheduling for the envelope with the given ID, -envelopes with the given message ID, -or all envelopes. -.It Cm resume mda -Resume deliveries to local users. -.It Cm resume mta -Resume relaying and deliveries to remote users. -.It Cm resume route Ar route-id -Resume routing on disabled route -.Ar route-id . -.It Cm resume smtp -Resume accepting incoming sessions. -.It Cm schedule Ar envelope-id | message-id | Cm all -Mark as ready for immediate delivery -a single envelope, -envelopes with the given message ID, -or all envelopes. -.It Cm show envelope Ar envelope-id -Display envelope content for the given ID. -.It Cm show hosts -Display the list of known remote MX hosts. -For each of them, it shows the IP address, the canonical hostname, -a reference count, the number of active connections to this host, -and the elapsed time since the last connection. -.It Cm show hoststats -Display status of last delivery for domains that have been active in the -last 4 hours. -It consists of the following fields, separated by a "|": -.Pp -.Bl -bullet -compact -.It -Domain. -.It -.Ux -timestamp of last delivery. -.It -Status of last delivery. -.El -.It Cm show message Ar envelope-id -Display message content for the given ID. -.It Cm show queue -Display information concerning envelopes that are currently in the queue. -Each line of output describes a single envelope. -It consists of the following fields, separated by a "|": -.Pp -.Bl -bullet -compact -.It -Envelope ID. -.It -Address family of the client which enqueued the mail. -.It -Type of delivery: one of "mta", "mda" or "bounce". -.It -Various flags on the envelope. -.It -Sender address (return path). -.It -The original recipient address. -.It -The destination address. -.It -Time of creation. -.It -Time of expiration. -.It -Time of last delivery or relaying attempt. -.It -Number of delivery or relaying attempts. -.It -Current runstate: either "pending" or "inflight" if -.Xr smtpd 8 -is running, or "offline" otherwise. -.It -Delay in seconds before the next attempt if pending, or time elapsed -if currently running. -This field is blank if -.Xr smtpd 8 -is not running. -.It -Error string for the last failed delivery or relay attempt. -.El -.It Cm show relays -Display the list of currently active relays and associated connectors. -For each relay, it shows a number of counters and information on its -internal state on a single line. -Then comes the list of connectors -(source addresses to connect from for this relay). -.It Cm show routes -Display status of routes currently known by -.Xr smtpd 8 . -Each line consists of a route number, a source address, a destination -address, a set of flags, the number of connections on this -route, the current penalty level which determines the amount of time -the route is disabled if an error occurs, and the delay before it -gets reactivated. -The following flags are defined: -.Pp -.Bl -tag -width xx -compact -.It D -The route is currently disabled. -.It N -The route is new. -No SMTP session has been established yet. -.It Q -The route has a timeout registered to lower its penalty level and possibly -reactivate or discard it. -.El -.It Cm show stats -Displays runtime statistics concerning -.Xr smtpd 8 . -.It Cm show status -Shows if MTA, MDA and SMTP systems are currently running or paused. -.It Cm spf walk -Recursively look up SPF records for the domains read from stdin. -For example: -.Bd -literal -offset indent -# smtpctl spf walk < domains.txt -.Ed -.It Cm trace Ar subsystem -Enables real-time tracing of -.Ar subsystem . -Supported subsystems are: -.Pp -.Bl -bullet -compact -.It -imsg -.It -io -.It -smtp (incoming sessions) -.It -filters -.It -mta (outgoing sessions) -.It -bounce -.It -scheduler -.It -expand (aliases/virtual/forward expansion) -.It -lookup (user/credentials lookups) -.It -stat -.It -rules (matched by incoming sessions) -.It -mproc -.It -all -.El -.It Cm unprofile Ar subsystem -Disables real-time profiling of -.Ar subsystem . -.It Cm untrace Ar subsystem -Disables real-time tracing of -.Ar subsystem . -.It Cm update table Ar name -Updates the contents of table -.Ar name , -for tables using the -.Dq file -backend. -.El -.Pp -When -.Nm smtpd -receives a message, it generates a -.Ar message-id -for the message, and one -.Ar envelope-id -per recipient. -The -.Ar message-id -is a 32-bit random identifier that is guaranteed to be -unique on the host system. -The -.Ar envelope-id -is a 64-bit unique identifier that encodes the -.Ar message-id -in the 32 upper bits and a random envelope identifier -in the 32 lower bits. -.Pp -A command which specifies a -.Ar message-id -applies to all recipients of a message; -a command which specifies an -.Ar envelope-id -applies to a specific recipient of a message. -.Sh FILES -.Bl -tag -width "/var/run/smtpd.sockXXX" -compact -.It Pa /var/run/smtpd.sock -.Ux Ns -domain -socket used for communication with -.Xr smtpd 8 . -.El -.Sh SEE ALSO -.Xr smtpd 8 -.Sh HISTORY -The -.Nm -program first appeared in -.Ox 4.6 . diff --git a/smtpd/smtpctl.c b/smtpd/smtpctl.c deleted file mode 100644 index 7dba4224..00000000 --- a/smtpd/smtpctl.c +++ /dev/null @@ -1,1469 +0,0 @@ -/* $OpenBSD: smtpctl.c,v 1.167 2020/02/24 16:16:07 millert Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2006 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> - * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> - * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/un.h> -#include <sys/wait.h> -#include <sys/stat.h> - -#include <net/if.h> -/* #include <net/if_media.h> */ -/* #include <net/if_types.h> */ -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fts.h> -#include <grp.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> -#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) -#include <vis.h> -#else -#include "bsd-vis.h" -#endif -#include <limits.h> - -#include "smtpd.h" -#include "parser.h" -#include "log.h" - -#ifndef PATH_GZCAT -#define PATH_GZCAT "/usr/bin/gzcat" -#endif -#define PATH_CAT "/bin/cat" -#define PATH_QUEUE "/queue" -#ifndef PATH_ENCRYPT -#define PATH_ENCRYPT "/usr/bin/encrypt" -#endif - -#ifndef HAVE_DB_API -#define makemap(x, y, z) 1 -#endif - - -int srv_connect(void); -int srv_connected(void); - -void usage(void); -static void show_queue_envelope(struct envelope *, int); -static void getflag(uint *, int, char *, char *, size_t); -static void display(const char *); -static int str_to_trace(const char *); -static int str_to_profile(const char *); -static void show_offline_envelope(uint64_t); -static int is_gzip_fp(FILE *); -static int is_encrypted_fp(FILE *); -static int is_encrypted_buffer(const char *); -static int is_gzip_buffer(const char *); -static FILE *offline_file(void); -static void sendmail_compat(int, char **); - -extern int spfwalk(int, struct parameter *); - -extern char *__progname; -int sendmail; -struct smtpd *env; -struct imsgbuf *ibuf; -struct imsg imsg; -char *rdata; -size_t rlen; -time_t now; - -struct queue_backend queue_backend_null; -struct queue_backend queue_backend_proc; -struct queue_backend queue_backend_ram; - -__dead void -usage(void) -{ - if (sendmail) - fprintf(stderr, "usage: %s [-tv] [-f from] [-F name] to ...\n", - __progname); - else - fprintf(stderr, "usage: %s command [argument ...]\n", - __progname); - exit(1); -} - -void stat_increment(const char *k, size_t v) -{ -} - -void stat_decrement(const char *k, size_t v) -{ -} - -int -srv_connect(void) -{ - struct sockaddr_un s_un; - int ctl_sock, saved_errno; - - /* connect to smtpd control socket */ - if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - err(1, "socket"); - - memset(&s_un, 0, sizeof(s_un)); - s_un.sun_family = AF_UNIX; - (void)strlcpy(s_un.sun_path, SMTPD_SOCKET, sizeof(s_un.sun_path)); - if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { - saved_errno = errno; - close(ctl_sock); - errno = saved_errno; - return (0); - } - - ibuf = xcalloc(1, sizeof(struct imsgbuf)); - imsg_init(ibuf, ctl_sock); - - return (1); -} - -int -srv_connected(void) -{ - return ibuf != NULL ? 1 : 0; -} - -FILE * -offline_file(void) -{ - char path[PATH_MAX]; - int fd; - FILE *fp; - - if (!bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL, - PATH_OFFLINE, (long long int) time(NULL))) - err(EX_UNAVAILABLE, "snprintf"); - - if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { - if (fd != -1) - unlink(path); - err(EX_UNAVAILABLE, "cannot create temporary file %s", path); - } - - if (fchmod(fd, 0600) == -1) { - unlink(path); - err(EX_SOFTWARE, "fchmod"); - } - - return fp; -} - - -static void -srv_flush(void) -{ - if (imsg_flush(ibuf) == -1) - err(1, "write error"); -} - -static void -srv_send(int msg, const void *data, size_t len) -{ - if (ibuf == NULL && !srv_connect()) - errx(1, "smtpd doesn't seem to be running"); - imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len); -} - -static void -srv_recv(int type) -{ - ssize_t n; - - srv_flush(); - - while (1) { - if ((n = imsg_get(ibuf, &imsg)) == -1) - errx(1, "imsg_get error"); - if (n) { - if (imsg.hdr.type == IMSG_CTL_FAIL && - imsg.hdr.peerid != 0 && - imsg.hdr.peerid != IMSG_VERSION) - errx(1, "incompatible smtpctl and smtpd"); - if (type != -1 && type != (int)imsg.hdr.type) - errx(1, "bad message type"); - rdata = imsg.data; - rlen = imsg.hdr.len - sizeof(imsg.hdr); - break; - } - - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - errx(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); - } -} - -static void -srv_read(void *dst, size_t sz) -{ - if (sz == 0) - return; - if (rlen < sz) - errx(1, "message too short"); - if (dst) - memmove(dst, rdata, sz); - rlen -= sz; - rdata += sz; -} - -static void -srv_get_int(int *i) -{ - srv_read(i, sizeof(*i)); -} - -static void -srv_get_time(time_t *t) -{ - srv_read(t, sizeof(*t)); -} - -static void -srv_get_evpid(uint64_t *evpid) -{ - srv_read(evpid, sizeof(*evpid)); -} - -static void -srv_get_string(const char **s) -{ - const char *end; - size_t len; - - if (rlen == 0) - errx(1, "message too short"); - - rlen -= 1; - if (*rdata++ == '\0') { - *s = NULL; - return; - } - - if (rlen == 0) - errx(1, "bogus string"); - - end = memchr(rdata, 0, rlen); - if (end == NULL) - errx(1, "unterminated string"); - - len = end + 1 - rdata; - - *s = rdata; - rlen -= len; - rdata += len; -} - -static void -srv_get_envelope(struct envelope *evp) -{ - uint64_t evpid; - const char *str; - - srv_get_evpid(&evpid); - srv_get_string(&str); - - envelope_load_buffer(evp, str, strlen(str)); - evp->id = evpid; -} - -static void -srv_end(void) -{ - if (rlen) - errx(1, "bogus data"); - imsg_free(&imsg); -} - -static int -srv_check_result(int verbose_) -{ - srv_recv(-1); - srv_end(); - - switch (imsg.hdr.type) { - case IMSG_CTL_OK: - if (verbose_) - printf("command succeeded\n"); - return (0); - case IMSG_CTL_FAIL: - if (verbose_) { - if (rlen) - printf("command failed: %s\n", rdata); - else - printf("command failed\n"); - } - return (1); - default: - errx(1, "wrong message in response: %u", imsg.hdr.type); - } - return (0); -} - -static int -srv_iter_messages(uint32_t *res) -{ - static uint32_t *msgids = NULL, from = 0; - static size_t n, curr; - static int done = 0; - - if (done) - return (0); - - if (msgids == NULL) { - srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from)); - srv_recv(IMSG_CTL_LIST_MESSAGES); - if (rlen == 0) { - srv_end(); - done = 1; - return (0); - } - msgids = malloc(rlen); - n = rlen / sizeof(*msgids); - srv_read(msgids, rlen); - srv_end(); - - curr = 0; - from = msgids[n - 1] + 1; - if (from == 0) - done = 1; - } - - *res = msgids[curr++]; - if (curr == n) { - free(msgids); - msgids = NULL; - } - - return (1); -} - -static int -srv_iter_envelopes(uint32_t msgid, struct envelope *evp) -{ - static uint32_t currmsgid = 0; - static uint64_t from = 0; - static int done = 0, need_send = 1, found; - int flags; - time_t nexttry; - - if (currmsgid != msgid) { - if (currmsgid != 0 && !done) - errx(1, "must finish current iteration first"); - currmsgid = msgid; - from = msgid_to_evpid(msgid); - done = 0; - found = 0; - need_send = 1; - } - - if (done) - return (0); - - again: - if (need_send) { - found = 0; - srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from)); - } - need_send = 0; - - srv_recv(IMSG_CTL_LIST_ENVELOPES); - if (rlen == 0) { - srv_end(); - if (!found || evpid_to_msgid(from) != msgid) { - done = 1; - return (0); - } - need_send = 1; - goto again; - } - - srv_get_int(&flags); - srv_get_time(&nexttry); - srv_get_envelope(evp); - srv_end(); - - evp->flags |= flags; - evp->nexttry = nexttry; - - from = evp->id + 1; - found++; - return (1); -} - -static int -srv_iter_evpids(uint32_t msgid, uint64_t *evpid, int *offset) -{ - static uint64_t *evpids = NULL, *tmp; - static int n, tmpalloc, alloc = 0; - struct envelope evp; - - if (*offset == 0) { - n = 0; - while (srv_iter_envelopes(msgid, &evp)) { - if (n == alloc) { - tmpalloc = alloc ? (alloc * 2) : 128; - tmp = recallocarray(evpids, alloc, tmpalloc, - sizeof(*evpids)); - if (tmp == NULL) - err(1, "recallocarray"); - evpids = tmp; - alloc = tmpalloc; - } - evpids[n++] = evp.id; - } - } - - if (*offset >= n) - return (0); - *evpid = evpids[*offset]; - *offset += 1; - return (1); -} - -static void -srv_foreach_envelope(struct parameter *argv, int ctl, size_t *total, size_t *ok) -{ - uint32_t msgid; - uint64_t evpid; - int i; - - *total = 0; - *ok = 0; - - if (argv == NULL) { - while (srv_iter_messages(&msgid)) { - i = 0; - while (srv_iter_evpids(msgid, &evpid, &i)) { - *total += 1; - srv_send(ctl, &evpid, sizeof(evpid)); - if (srv_check_result(0) == 0) - *ok += 1; - } - } - } else if (argv->type == P_MSGID) { - i = 0; - while (srv_iter_evpids(argv->u.u_msgid, &evpid, &i)) { - srv_send(ctl, &evpid, sizeof(evpid)); - if (srv_check_result(0) == 0) - *ok += 1; - } - } else { - *total += 1; - srv_send(ctl, &argv->u.u_evpid, sizeof(evpid)); - if (srv_check_result(0) == 0) - *ok += 1; - } -} - -static void -srv_show_cmd(int cmd, const void *data, size_t len) -{ - int done = 0; - - srv_send(cmd, data, len); - - do { - srv_recv(cmd); - if (rlen) { - printf("%s\n", rdata); - srv_read(NULL, rlen); - } - else - done = 1; - srv_end(); - } while (!done); -} - -static void -droppriv(void) -{ - struct passwd *pw; - - if (geteuid()) - return; - - if ((pw = getpwnam(SMTPD_USER)) == NULL) - errx(1, "unknown user " SMTPD_USER); - - if ((setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))) - err(1, "cannot drop privileges"); -} - -static int -do_permission_denied(int argc, struct parameter *argv) -{ - errx(1, "need root privileges"); -} - -static int -do_log_brief(int argc, struct parameter *argv) -{ - int v = 0; - - srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_log_verbose(int argc, struct parameter *argv) -{ - int v = TRACE_DEBUG; - - srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_monitor(int argc, struct parameter *argv) -{ - struct stat_digest last, digest; - size_t count; - - memset(&last, 0, sizeof(last)); - count = 0; - - while (1) { - srv_send(IMSG_CTL_GET_DIGEST, NULL, 0); - srv_recv(IMSG_CTL_GET_DIGEST); - srv_read(&digest, sizeof(digest)); - srv_end(); - - if (count % 25 == 0) { - if (count != 0) - printf("\n"); - printf("--- client --- " - "-- envelope -- " - "---- relay/delivery --- " - "------- misc -------\n" - "curr conn disc " - "curr enq deq " - "ok tmpfail prmfail loop " - "expire remove bounce\n"); - } - printf("%4zu %4zu %4zu " - "%4zu %4zu %4zu " - "%4zu %4zu %4zu %4zu " - "%4zu %4zu %4zu\n", - digest.clt_connect - digest.clt_disconnect, - digest.clt_connect - last.clt_connect, - digest.clt_disconnect - last.clt_disconnect, - - digest.evp_enqueued - digest.evp_dequeued, - digest.evp_enqueued - last.evp_enqueued, - digest.evp_dequeued - last.evp_dequeued, - - digest.dlv_ok - last.dlv_ok, - digest.dlv_tempfail - last.dlv_tempfail, - digest.dlv_permfail - last.dlv_permfail, - digest.dlv_loop - last.dlv_loop, - - digest.evp_expired - last.evp_expired, - digest.evp_removed - last.evp_removed, - digest.evp_bounce - last.evp_bounce); - - last = digest; - count++; - sleep(1); - } - - return (0); -} - -static int -do_pause_envelope(int argc, struct parameter *argv) -{ - size_t total, ok; - - srv_foreach_envelope(argv, IMSG_CTL_PAUSE_EVP, &total, &ok); - printf("%zu envelope%s paused\n", ok, (ok > 1) ? "s" : ""); - - return (0); -} - -static int -do_pause_mda(int argc, struct parameter *argv) -{ - srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0); - return srv_check_result(1); -} - -static int -do_pause_mta(int argc, struct parameter *argv) -{ - srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0); - return srv_check_result(1); -} - -static int -do_pause_smtp(int argc, struct parameter *argv) -{ - srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0); - return srv_check_result(1); -} - -static int -do_profile(int argc, struct parameter *argv) -{ - int v; - - v = str_to_profile(argv[0].u.u_str); - - srv_send(IMSG_CTL_PROFILE_ENABLE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_remove(int argc, struct parameter *argv) -{ - size_t total, ok; - - srv_foreach_envelope(argv, IMSG_CTL_REMOVE, &total, &ok); - printf("%zu envelope%s removed\n", ok, (ok > 1) ? "s" : ""); - - return (0); -} - -static int -do_resume_envelope(int argc, struct parameter *argv) -{ - size_t total, ok; - - srv_foreach_envelope(argv, IMSG_CTL_RESUME_EVP, &total, &ok); - printf("%zu envelope%s resumed\n", ok, (ok > 1) ? "s" : ""); - - return (0); -} - -static int -do_resume_mda(int argc, struct parameter *argv) -{ - srv_send(IMSG_CTL_RESUME_MDA, NULL, 0); - return srv_check_result(1); -} - -static int -do_resume_mta(int argc, struct parameter *argv) -{ - srv_send(IMSG_CTL_RESUME_MTA, NULL, 0); - return srv_check_result(1); -} - -static int -do_resume_route(int argc, struct parameter *argv) -{ - uint64_t v; - - if (argc == 0) - v = 0; - else - v = argv[0].u.u_routeid; - - srv_send(IMSG_CTL_RESUME_ROUTE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_resume_smtp(int argc, struct parameter *argv) -{ - srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0); - return srv_check_result(1); -} - -static int -do_schedule(int argc, struct parameter *argv) -{ - size_t total, ok; - - srv_foreach_envelope(argv, IMSG_CTL_SCHEDULE, &total, &ok); - printf("%zu envelope%s scheduled\n", ok, (ok > 1) ? "s" : ""); - - return (0); -} - -static int -do_show_envelope(int argc, struct parameter *argv) -{ - char buf[PATH_MAX]; - - if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64, - PATH_SPOOL, - PATH_QUEUE, - (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24, - evpid_to_msgid(argv[0].u.u_evpid), - argv[0].u.u_evpid)) - errx(1, "unable to retrieve envelope"); - - display(buf); - - return (0); -} - -static int -do_show_hoststats(int argc, struct parameter *argv) -{ - srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTSTATS, NULL, 0); - - return (0); -} - -static int -do_show_message(int argc, struct parameter *argv) -{ - char buf[PATH_MAX]; - uint32_t msgid; - - if (argv[0].type == P_EVPID) - msgid = evpid_to_msgid(argv[0].u.u_evpid); - else - msgid = argv[0].u.u_msgid; - - if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message", - PATH_SPOOL, - PATH_QUEUE, - (msgid & 0xff000000) >> 24, - msgid)) - errx(1, "unable to retrieve message"); - - display(buf); - - return (0); -} - -static int -do_show_queue(int argc, struct parameter *argv) -{ - struct envelope evp; - uint32_t msgid; - FTS *fts; - FTSENT *ftse; - char *qpath[] = {"/queue", NULL}; - char *tmp; - uint64_t evpid; - - now = time(NULL); - - if (!srv_connect()) { - log_init(1, LOG_MAIL); - queue_init("fs", 0); - if (chroot(PATH_SPOOL) == -1 || chdir("/") == -1) - err(1, "%s", PATH_SPOOL); - fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL); - if (fts == NULL) - err(1, "%s/queue", PATH_SPOOL); - - while ((ftse = fts_read(fts)) != NULL) { - switch (ftse->fts_info) { - case FTS_DP: - case FTS_DNR: - break; - case FTS_F: - tmp = NULL; - evpid = strtoull(ftse->fts_name, &tmp, 16); - if (tmp && *tmp != '\0') - break; - show_offline_envelope(evpid); - } - } - - fts_close(fts); - return (0); - } - - if (argc == 0) { - msgid = 0; - while (srv_iter_messages(&msgid)) - while (srv_iter_envelopes(msgid, &evp)) - show_queue_envelope(&evp, 1); - } else if (argv[0].type == P_MSGID) { - while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) - show_queue_envelope(&evp, 1); - } - - return (0); -} - -static int -do_show_hosts(int argc, struct parameter *argv) -{ - srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTS, NULL, 0); - - return (0); -} - -static int -do_show_relays(int argc, struct parameter *argv) -{ - srv_show_cmd(IMSG_CTL_MTA_SHOW_RELAYS, NULL, 0); - - return (0); -} - -static int -do_show_routes(int argc, struct parameter *argv) -{ - srv_show_cmd(IMSG_CTL_MTA_SHOW_ROUTES, NULL, 0); - - return (0); -} - -static int -do_show_stats(int argc, struct parameter *argv) -{ - struct stat_kv kv; - time_t duration; - - memset(&kv, 0, sizeof kv); - - while (1) { - srv_send(IMSG_CTL_GET_STATS, &kv, sizeof kv); - srv_recv(IMSG_CTL_GET_STATS); - srv_read(&kv, sizeof(kv)); - srv_end(); - - if (kv.iter == NULL) - break; - - if (strcmp(kv.key, "uptime") == 0) { - duration = time(NULL) - kv.val.u.counter; - printf("uptime=%lld\n", (long long)duration); - printf("uptime.human=%s\n", - duration_to_text(duration)); - } - else { - switch (kv.val.type) { - case STAT_COUNTER: - printf("%s=%zd\n", - kv.key, kv.val.u.counter); - break; - case STAT_TIMESTAMP: - printf("%s=%" PRId64 "\n", - kv.key, (int64_t)kv.val.u.timestamp); - break; - case STAT_TIMEVAL: - printf("%s=%lld.%lld\n", - kv.key, (long long)kv.val.u.tv.tv_sec, - (long long)kv.val.u.tv.tv_usec); - break; - case STAT_TIMESPEC: - printf("%s=%lld.%06ld\n", - kv.key, - (long long)kv.val.u.ts.tv_sec * 1000000 + - kv.val.u.ts.tv_nsec / 1000000, - kv.val.u.ts.tv_nsec % 1000000); - break; - } - } - } - - return (0); -} - -static int -do_show_status(int argc, struct parameter *argv) -{ - uint32_t sc_flags; - - srv_send(IMSG_CTL_SHOW_STATUS, NULL, 0); - srv_recv(IMSG_CTL_SHOW_STATUS); - srv_read(&sc_flags, sizeof(sc_flags)); - srv_end(); - printf("MDA %s\n", - (sc_flags & SMTPD_MDA_PAUSED) ? "paused" : "running"); - printf("MTA %s\n", - (sc_flags & SMTPD_MTA_PAUSED) ? "paused" : "running"); - printf("SMTP %s\n", - (sc_flags & SMTPD_SMTP_PAUSED) ? "paused" : "running"); - return (0); -} - -static int -do_trace(int argc, struct parameter *argv) -{ - int v; - - v = str_to_trace(argv[0].u.u_str); - - srv_send(IMSG_CTL_TRACE_ENABLE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_unprofile(int argc, struct parameter *argv) -{ - int v; - - v = str_to_profile(argv[0].u.u_str); - - srv_send(IMSG_CTL_PROFILE_DISABLE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_untrace(int argc, struct parameter *argv) -{ - int v; - - v = str_to_trace(argv[0].u.u_str); - - srv_send(IMSG_CTL_TRACE_DISABLE, &v, sizeof(v)); - return srv_check_result(1); -} - -static int -do_update_table(int argc, struct parameter *argv) -{ - const char *name = argv[0].u.u_str; - - srv_send(IMSG_CTL_UPDATE_TABLE, name, strlen(name) + 1); - return srv_check_result(1); -} - -static int -do_encrypt(int argc, struct parameter *argv) -{ - const char *p = NULL; - - droppriv(); - - if (argv) - p = argv[0].u.u_str; - execl(PATH_ENCRYPT, "encrypt", "--", p, (char *)NULL); - errx(1, "execl"); -} - -static int -do_block_mta(int argc, struct parameter *argv) -{ - struct ibuf *m; - - if (ibuf == NULL && !srv_connect()) - errx(1, "smtpd doesn't seem to be running"); - m = imsg_create(ibuf, IMSG_CTL_MTA_BLOCK, IMSG_VERSION, 0, - sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); - if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) - errx(1, "imsg_add"); - if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) - errx(1, "imsg_add"); - imsg_close(ibuf, m); - - return srv_check_result(1); -} - -static int -do_unblock_mta(int argc, struct parameter *argv) -{ - struct ibuf *m; - - if (ibuf == NULL && !srv_connect()) - errx(1, "smtpd doesn't seem to be running"); - - m = imsg_create(ibuf, IMSG_CTL_MTA_UNBLOCK, IMSG_VERSION, 0, - sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); - if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) - errx(1, "imsg_add"); - if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) - errx(1, "imsg_add"); - imsg_close(ibuf, m); - - return srv_check_result(1); -} - -static int -do_show_mta_block(int argc, struct parameter *argv) -{ - srv_show_cmd(IMSG_CTL_MTA_SHOW_BLOCK, NULL, 0); - - return (0); -} - -static int -do_discover(int argc, struct parameter *argv) -{ - uint64_t evpid; - uint32_t msgid; - size_t n_evp; - - if (ibuf == NULL && !srv_connect()) - errx(1, "smtpd doesn't seem to be running"); - - if (argv[0].type == P_EVPID) { - evpid = argv[0].u.u_evpid; - srv_send(IMSG_CTL_DISCOVER_EVPID, &evpid, sizeof evpid); - srv_recv(IMSG_CTL_DISCOVER_EVPID); - } else { - msgid = argv[0].u.u_msgid; - srv_send(IMSG_CTL_DISCOVER_MSGID, &msgid, sizeof msgid); - srv_recv(IMSG_CTL_DISCOVER_MSGID); - } - - if (rlen == 0) { - srv_end(); - return (0); - } else { - srv_read(&n_evp, sizeof n_evp); - srv_end(); - } - - printf("%zu envelope%s discovered\n", n_evp, (n_evp != 1) ? "s" : ""); - return (0); -} - -static int -do_spf_walk(int argc, struct parameter *argv) -{ - droppriv(); - - return spfwalk(argc, argv); -} - -#define cmd_install_priv(s, f) \ - cmd_install((s), privileged ? (f) : do_permission_denied) - -int -main(int argc, char **argv) -{ - gid_t gid; - struct group *gr; - int privileged; - char *argv_mailq[] = { "show", "queue", NULL }; - -#ifndef HAVE___PROGNAME - __progname = ssh_get_progname(argv[0]); -#endif - - /* check that smtpctl was installed setgid */ - if ((gr = getgrnam(SMTPD_QUEUE_GROUP)) == NULL) - errx(1, "unknown group %s", SMTPD_QUEUE_GROUP); - else if (gr->gr_gid != getegid()) - errx(1, "this program must be setgid %s", SMTPD_QUEUE_GROUP); - - sendmail_compat(argc, argv); - privileged = geteuid() == 0; - - gid = getgid(); - if (setresgid(gid, gid, gid) == -1) - err(1, "setresgid"); - - /* Privileged commands */ - cmd_install_priv("discover <evpid>", do_discover); - cmd_install_priv("discover <msgid>", do_discover); - cmd_install_priv("pause mta from <addr> for <str>", do_block_mta); - cmd_install_priv("resume mta from <addr> for <str>", do_unblock_mta); - cmd_install_priv("show mta paused", do_show_mta_block); - cmd_install_priv("log brief", do_log_brief); - cmd_install_priv("log verbose", do_log_verbose); - cmd_install_priv("monitor", do_monitor); - cmd_install_priv("pause envelope <evpid>", do_pause_envelope); - cmd_install_priv("pause envelope <msgid>", do_pause_envelope); - cmd_install_priv("pause envelope all", do_pause_envelope); - cmd_install_priv("pause mda", do_pause_mda); - cmd_install_priv("pause mta", do_pause_mta); - cmd_install_priv("pause smtp", do_pause_smtp); - cmd_install_priv("profile <str>", do_profile); - cmd_install_priv("remove <evpid>", do_remove); - cmd_install_priv("remove <msgid>", do_remove); - cmd_install_priv("remove all", do_remove); - cmd_install_priv("resume envelope <evpid>", do_resume_envelope); - cmd_install_priv("resume envelope <msgid>", do_resume_envelope); - cmd_install_priv("resume envelope all", do_resume_envelope); - cmd_install_priv("resume mda", do_resume_mda); - cmd_install_priv("resume mta", do_resume_mta); - cmd_install_priv("resume route <routeid>", do_resume_route); - cmd_install_priv("resume smtp", do_resume_smtp); - cmd_install_priv("schedule <msgid>", do_schedule); - cmd_install_priv("schedule <evpid>", do_schedule); - cmd_install_priv("schedule all", do_schedule); - cmd_install_priv("show envelope <evpid>", do_show_envelope); - cmd_install_priv("show hoststats", do_show_hoststats); - cmd_install_priv("show message <msgid>", do_show_message); - cmd_install_priv("show message <evpid>", do_show_message); - cmd_install_priv("show queue", do_show_queue); - cmd_install_priv("show queue <msgid>", do_show_queue); - cmd_install_priv("show hosts", do_show_hosts); - cmd_install_priv("show relays", do_show_relays); - cmd_install_priv("show routes", do_show_routes); - cmd_install_priv("show stats", do_show_stats); - cmd_install_priv("show status", do_show_status); - cmd_install_priv("trace <str>", do_trace); - cmd_install_priv("unprofile <str>", do_unprofile); - cmd_install_priv("untrace <str>", do_untrace); - cmd_install_priv("update table <str>", do_update_table); - - /* Unprivileged commands */ - cmd_install("encrypt", do_encrypt); - cmd_install("encrypt <str>", do_encrypt); - cmd_install("spf walk", do_spf_walk); - - if (strcmp(__progname, "mailq") == 0) - return cmd_run(2, argv_mailq); - if (strcmp(__progname, "smtpctl") == 0) - return cmd_run(argc - 1, argv + 1); - - errx(1, "unsupported mode"); - return (0); -} - -void -sendmail_compat(int argc, char **argv) -{ - FILE *offlinefp = NULL; - gid_t gid; - int i, r; - - if (strcmp(__progname, "sendmail") == 0 || - strcmp(__progname, "send-mail") == 0) { - /* - * determine whether we are called with flags - * that should invoke makemap/newaliases. - */ - for (i = 1; i < argc; i++) - if (strncmp(argv[i], "-bi", 3) == 0) - exit(makemap(P_SENDMAIL, argc, argv)); - - if (!srv_connect()) - offlinefp = offline_file(); - - gid = getgid(); - if (setresgid(gid, gid, gid) == -1) - err(1, "setresgid"); - -#if HAVE_PLEDGE - /* we'll reduce further down the road */ - if (pledge("stdio rpath wpath cpath tmppath flock " - "dns getpw recvfd", NULL) == -1) - err(1, "pledge"); -#endif - - sendmail = 1; - exit(enqueue(argc, argv, offlinefp)); - } else if (strcmp(__progname, "makemap") == 0) - exit(makemap(P_MAKEMAP, argc, argv)); - else if (strcmp(__progname, "newaliases") == 0) { - r = makemap(P_NEWALIASES, argc, argv); - /* - * if server is available, notify of table update. - * only makes sense for static tables AND if server is up. - */ - if (srv_connect()) { - srv_send(IMSG_CTL_UPDATE_TABLE, "aliases", strlen("aliases") + 1); - srv_check_result(0); - } - exit(r); - } -} - -static void -show_queue_envelope(struct envelope *e, int online) -{ - const char *src = "?", *agent = "?"; - char status[128], runstate[128], errline[LINE_MAX]; - - status[0] = '\0'; - - getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status)); - getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status)); - getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status)); - getflag(&e->flags, EF_SUSPEND, "suspend", status, sizeof(status)); - getflag(&e->flags, EF_HOLD, "hold", status, sizeof(status)); - - if (online) { - if (e->flags & EF_PENDING) - (void)snprintf(runstate, sizeof runstate, "pending|%zd", - (ssize_t)(e->nexttry - now)); - else if (e->flags & EF_INFLIGHT) - (void)snprintf(runstate, sizeof runstate, - "inflight|%zd", (ssize_t)(now - e->lasttry)); - else - (void)snprintf(runstate, sizeof runstate, "invalid|"); - e->flags &= ~(EF_PENDING|EF_INFLIGHT); - } - else - (void)strlcpy(runstate, "offline|", sizeof runstate); - - if (e->flags) - errx(1, "%016" PRIx64 ": unexpected flags 0x%04x", e->id, - e->flags); - - if (status[0]) - status[strlen(status) - 1] = '\0'; - - if (e->type == D_MDA) - agent = "mda"; - else if (e->type == D_MTA) - agent = "mta"; - else if (e->type == D_BOUNCE) - agent = "bounce"; - - if (e->ss.ss_family == AF_LOCAL) - src = "local"; - else if (e->ss.ss_family == AF_INET) - src = "inet4"; - else if (e->ss.ss_family == AF_INET6) - src = "inet6"; - - strnvis(errline, e->errorline, sizeof(errline), 0); - - printf("%016"PRIx64 - "|%s|%s|%s|%s@%s|%s@%s|%s@%s" - "|%zu|%zu|%zu|%zu|%s|%s\n", - - e->id, - - src, - agent, - status, - e->sender.user, e->sender.domain, - e->rcpt.user, e->rcpt.domain, - e->dest.user, e->dest.domain, - - (size_t) e->creation, - (size_t) (e->creation + e->ttl), - (size_t) e->lasttry, - (size_t) e->retry, - runstate, - errline); -} - -static void -getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len) -{ - if (*bitmap & bit) { - *bitmap &= ~bit; - (void)strlcat(buf, bitstr, len); - (void)strlcat(buf, ",", len); - } -} - -static void -show_offline_envelope(uint64_t evpid) -{ - FILE *fp = NULL; - char pathname[PATH_MAX]; - size_t plen; - char *p; - size_t buflen; - char buffer[sizeof(struct envelope)]; - - struct envelope evp; - - if (!bsnprintf(pathname, sizeof pathname, - "/queue/%02x/%08x/%016"PRIx64, - (evpid_to_msgid(evpid) & 0xff000000) >> 24, - evpid_to_msgid(evpid), evpid)) - goto end; - fp = fopen(pathname, "r"); - if (fp == NULL) - goto end; - - buflen = fread(buffer, 1, sizeof (buffer) - 1, fp); - p = buffer; - plen = buflen; - buffer[buflen] = '\0'; - - if (is_encrypted_buffer(p)) { - warnx("offline encrypted queue is not supported yet"); - goto end; - } - - if (is_gzip_buffer(p)) { - warnx("offline compressed queue is not supported yet"); - goto end; - } - - if (!envelope_load_buffer(&evp, p, plen)) - goto end; - evp.id = evpid; - show_queue_envelope(&evp, 0); - -end: - if (fp) - fclose(fp); -} - -static void -display(const char *s) -{ - FILE *fp; - char *key; - int gzipped; - char *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1; - - if ((fp = fopen(s, "r")) == NULL) - err(1, "fopen"); - - if (is_encrypted_fp(fp)) { - int i; - FILE *ofp = NULL; - - if ((ofp = tmpfile()) == NULL) - err(1, "tmpfile"); - - for (i = 0; i < 3; i++) { - key = getpass("key> "); - if (crypto_setup(key, strlen(key))) - break; - } - if (i == 3) - errx(1, "crypto-setup: invalid key"); - - if (!crypto_decrypt_file(fp, ofp)) { - printf("object is encrypted: %s\n", key); - exit(1); - } - - fclose(fp); - fp = ofp; - fseek(fp, 0, SEEK_SET); - } - gzipped = is_gzip_fp(fp); - - lseek(fileno(fp), 0, SEEK_SET); - (void)dup2(fileno(fp), STDIN_FILENO); - if (gzipped) - execl(PATH_GZCAT, gzcat_argv0, (char *)NULL); - else - execl(PATH_CAT, "cat", (char *)NULL); - err(1, "execl"); -} - -static int -str_to_trace(const char *str) -{ - if (!strcmp(str, "imsg")) - return TRACE_IMSG; - if (!strcmp(str, "io")) - return TRACE_IO; - if (!strcmp(str, "smtp")) - return TRACE_SMTP; - if (!strcmp(str, "filters")) - return TRACE_FILTERS; - if (!strcmp(str, "mta")) - return TRACE_MTA; - if (!strcmp(str, "bounce")) - return TRACE_BOUNCE; - if (!strcmp(str, "scheduler")) - return TRACE_SCHEDULER; - if (!strcmp(str, "lookup")) - return TRACE_LOOKUP; - if (!strcmp(str, "stat")) - return TRACE_STAT; - if (!strcmp(str, "rules")) - return TRACE_RULES; - if (!strcmp(str, "mproc")) - return TRACE_MPROC; - if (!strcmp(str, "expand")) - return TRACE_EXPAND; - if (!strcmp(str, "all")) - return ~TRACE_DEBUG; - errx(1, "invalid trace keyword: %s", str); - return (0); -} - -static int -str_to_profile(const char *str) -{ - if (!strcmp(str, "imsg")) - return PROFILE_IMSG; - if (!strcmp(str, "queue")) - return PROFILE_QUEUE; - errx(1, "invalid profile keyword: %s", str); - return (0); -} - -static int -is_gzip_buffer(const char *buffer) -{ - uint16_t magic; - - memcpy(&magic, buffer, sizeof magic); -#define GZIP_MAGIC 0x8b1f - return (magic == GZIP_MAGIC); -} - -static int -is_gzip_fp(FILE *fp) -{ - uint8_t magic[2]; - int ret = 0; - - if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) - goto end; - - ret = is_gzip_buffer((const char *)&magic); -end: - fseek(fp, 0, SEEK_SET); - return ret; -} - - -/* XXX */ -/* - * queue supports transparent encryption. - * encrypted chunks are prefixed with an API version byte - * which we ensure is unambiguous with gzipped / plain - * objects. - */ - -static int -is_encrypted_buffer(const char *buffer) -{ - uint8_t magic; - - magic = *buffer; -#define ENCRYPTION_MAGIC 0x1 - return (magic == ENCRYPTION_MAGIC); -} - -static int -is_encrypted_fp(FILE *fp) -{ - uint8_t magic; - int ret = 0; - - if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) - goto end; - - ret = is_encrypted_buffer((const char *)&magic); -end: - fseek(fp, 0, SEEK_SET); - return ret; -} diff --git a/smtpd/smtpctl/Makefile b/smtpd/smtpctl/Makefile deleted file mode 100644 index ef8148be..00000000 --- a/smtpd/smtpctl/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -# $OpenBSD: Makefile,v 1.47 2018/07/03 01:34:43 mortimer Exp $ - -.PATH: ${.CURDIR}/.. - -PROG= smtpctl -BINOWN= root -BINGRP= _smtpq - -BINMODE?=2555 - -BINDIR= /usr/sbin -MAN= smtpctl.8 aliases.5 forward.5 makemap.8 newaliases.8 - -CFLAGS+= -fstack-protector-all -CFLAGS+= -I${.CURDIR}/.. -CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes -CFLAGS+= -Wmissing-declarations -CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual -CFLAGS+= -Wsign-compare -CFLAGS+= -Werror-implicit-function-declaration -CFLAGS+= -DNO_IO -CFLAGS+= -DCONFIG_MINIMUM -YFLAGS= - -SRCS= enqueue.c -SRCS+= parser.c -SRCS+= log.c -SRCS+= envelope.c -SRCS+= crypto.c -SRCS+= queue_backend.c -SRCS+= queue_fs.c -SRCS+= smtpctl.c -SRCS+= util.c -SRCS+= compress_backend.c -SRCS+= compress_gzip.c -SRCS+= to.c -SRCS+= expand.c -SRCS+= tree.c -SRCS+= config.c -SRCS+= dict.c -SRCS+= aliases.c -SRCS+= limit.c -SRCS+= makemap.c -SRCS+= parse.y -SRCS+= mailaddr.c -SRCS+= table.c -SRCS+= table_static.c -SRCS+= table_db.c -SRCS+= table_getpwnam.c -SRCS+= table_proc.c -SRCS+= unpack_dns.c -SRCS+= spfwalk.c - -LDADD+= -levent -lutil -lz -lcrypto -DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBZ} ${LIBCRYPTO} -.include <bsd.prog.mk> diff --git a/smtpd/smtpd-api.h b/smtpd/smtpd-api.h deleted file mode 100644 index f83edd05..00000000 --- a/smtpd/smtpd-api.h +++ /dev/null @@ -1,290 +0,0 @@ -/* $OpenBSD: smtpd-api.h,v 1.36 2018/12/23 16:06:24 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SMTPD_API_H_ -#define _SMTPD_API_H_ - -#include "dict.h" -#include "tree.h" - -struct mailaddr { - char user[SMTPD_MAXLOCALPARTSIZE]; - char domain[SMTPD_MAXDOMAINPARTSIZE]; -}; - -#define PROC_QUEUE_API_VERSION 2 - -enum { - PROC_QUEUE_OK, - PROC_QUEUE_FAIL, - PROC_QUEUE_INIT, - PROC_QUEUE_CLOSE, - PROC_QUEUE_MESSAGE_CREATE, - PROC_QUEUE_MESSAGE_DELETE, - PROC_QUEUE_MESSAGE_COMMIT, - PROC_QUEUE_MESSAGE_FD_R, - PROC_QUEUE_ENVELOPE_CREATE, - PROC_QUEUE_ENVELOPE_DELETE, - PROC_QUEUE_ENVELOPE_LOAD, - PROC_QUEUE_ENVELOPE_UPDATE, - PROC_QUEUE_ENVELOPE_WALK, -}; - -#define PROC_SCHEDULER_API_VERSION 2 - -struct scheduler_info; - -enum { - PROC_SCHEDULER_OK, - PROC_SCHEDULER_FAIL, - PROC_SCHEDULER_INIT, - PROC_SCHEDULER_INSERT, - PROC_SCHEDULER_COMMIT, - PROC_SCHEDULER_ROLLBACK, - PROC_SCHEDULER_UPDATE, - PROC_SCHEDULER_DELETE, - PROC_SCHEDULER_HOLD, - PROC_SCHEDULER_RELEASE, - PROC_SCHEDULER_BATCH, - PROC_SCHEDULER_MESSAGES, - PROC_SCHEDULER_ENVELOPES, - PROC_SCHEDULER_SCHEDULE, - PROC_SCHEDULER_REMOVE, - PROC_SCHEDULER_SUSPEND, - PROC_SCHEDULER_RESUME, -}; - -enum envelope_flags { - EF_AUTHENTICATED = 0x01, - EF_BOUNCE = 0x02, - EF_INTERNAL = 0x04, /* Internal expansion forward */ - - /* runstate, not saved on disk */ - - EF_PENDING = 0x10, - EF_INFLIGHT = 0x20, - EF_SUSPEND = 0x40, - EF_HOLD = 0x80, -}; - -struct evpstate { - uint64_t evpid; - uint16_t flags; - uint16_t retry; - time_t time; -}; - -enum delivery_type { - D_MDA, - D_MTA, - D_BOUNCE, -}; - -struct scheduler_info { - uint64_t evpid; - enum delivery_type type; - uint16_t retry; - time_t creation; - time_t ttl; - time_t lasttry; - time_t lastbounce; - time_t nexttry; -}; - -#define SCHED_REMOVE 0x01 -#define SCHED_EXPIRE 0x02 -#define SCHED_UPDATE 0x04 -#define SCHED_BOUNCE 0x08 -#define SCHED_MDA 0x10 -#define SCHED_MTA 0x20 - -#define PROC_TABLE_API_VERSION 2 - -struct table_open_params { - uint32_t version; - char name[LINE_MAX]; -}; - -enum table_service { - K_NONE = 0x000, - K_ALIAS = 0x001, /* returns struct expand */ - K_DOMAIN = 0x002, /* returns struct destination */ - K_CREDENTIALS = 0x004, /* returns struct credentials */ - K_NETADDR = 0x008, /* returns struct netaddr */ - K_USERINFO = 0x010, /* returns struct userinfo */ - K_SOURCE = 0x020, /* returns struct source */ - K_MAILADDR = 0x040, /* returns struct mailaddr */ - K_ADDRNAME = 0x080, /* returns struct addrname */ - K_MAILADDRMAP = 0x100, /* returns struct maddrmap */ - K_RELAYHOST = 0x200, /* returns struct relayhost */ - K_STRING = 0x400, - K_REGEX = 0x800, -}; -#define K_ANY 0xfff - -enum { - PROC_TABLE_OK, - PROC_TABLE_FAIL, - PROC_TABLE_OPEN, - PROC_TABLE_CLOSE, - PROC_TABLE_UPDATE, - PROC_TABLE_CHECK, - PROC_TABLE_LOOKUP, - PROC_TABLE_FETCH, -}; - -enum enhanced_status_code { - /* 0.0 */ - ESC_OTHER_STATUS = 00, - - /* 1.x */ - ESC_OTHER_ADDRESS_STATUS = 10, - ESC_BAD_DESTINATION_MAILBOX_ADDRESS = 11, - ESC_BAD_DESTINATION_SYSTEM_ADDRESS = 12, - ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX = 13, - ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS = 14, - ESC_DESTINATION_ADDRESS_VALID = 15, - ESC_DESTINATION_MAILBOX_HAS_MOVED = 16, - ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX = 17, - ESC_BAD_SENDER_SYSTEM_ADDRESS = 18, - - /* 2.x */ - ESC_OTHER_MAILBOX_STATUS = 20, - ESC_MAILBOX_DISABLED = 21, - ESC_MAILBOX_FULL = 22, - ESC_MESSAGE_LENGTH_TOO_LARGE = 23, - ESC_MAILING_LIST_EXPANSION_PROBLEM = 24, - - /* 3.x */ - ESC_OTHER_MAIL_SYSTEM_STATUS = 30, - ESC_MAIL_SYSTEM_FULL = 31, - ESC_SYSTEM_NOT_ACCEPTING_MESSAGES = 32, - ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES = 33, - ESC_MESSAGE_TOO_BIG_FOR_SYSTEM = 34, - ESC_SYSTEM_INCORRECTLY_CONFIGURED = 35, - - /* 4.x */ - ESC_OTHER_NETWORK_ROUTING_STATUS = 40, - ESC_NO_ANSWER_FROM_HOST = 41, - ESC_BAD_CONNECTION = 42, - ESC_DIRECTORY_SERVER_FAILURE = 43, - ESC_UNABLE_TO_ROUTE = 44, - ESC_MAIL_SYSTEM_CONGESTION = 45, - ESC_ROUTING_LOOP_DETECTED = 46, - ESC_DELIVERY_TIME_EXPIRED = 47, - - /* 5.x */ - ESC_INVALID_RECIPIENT = 50, - ESC_INVALID_COMMAND = 51, - ESC_SYNTAX_ERROR = 52, - ESC_TOO_MANY_RECIPIENTS = 53, - ESC_INVALID_COMMAND_ARGUMENTS = 54, - ESC_WRONG_PROTOCOL_VERSION = 55, - - /* 6.x */ - ESC_OTHER_MEDIA_ERROR = 60, - ESC_MEDIA_NOT_SUPPORTED = 61, - ESC_CONVERSION_REQUIRED_AND_PROHIBITED = 62, - ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED = 63, - ESC_CONVERSION_WITH_LOSS_PERFORMED = 64, - ESC_CONVERSION_FAILED = 65, - - /* 7.x */ - ESC_OTHER_SECURITY_STATUS = 70, - ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED = 71, - ESC_MAILING_LIST_EXPANSION_PROHIBITED = 72, - ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE = 73, - ESC_SECURITY_FEATURES_NOT_SUPPORTED = 74, - ESC_CRYPTOGRAPHIC_FAILURE = 75, - ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED = 76, - ESC_MESSAGE_INTEGRITY_FAILURE = 77, -}; - -enum enhanced_status_class { - ESC_STATUS_OK = 2, - ESC_STATUS_TEMPFAIL = 4, - ESC_STATUS_PERMFAIL = 5, -}; - -static inline uint32_t -evpid_to_msgid(uint64_t evpid) -{ - return (evpid >> 32); -} - -static inline uint64_t -msgid_to_evpid(uint32_t msgid) -{ - return ((uint64_t)msgid << 32); -} - - -/* esc.c */ -const char *esc_code(enum enhanced_status_class, enum enhanced_status_code); -const char *esc_description(enum enhanced_status_code); - - -/* queue */ -void queue_api_on_close(int(*)(void)); -void queue_api_on_message_create(int(*)(uint32_t *)); -void queue_api_on_message_commit(int(*)(uint32_t, const char*)); -void queue_api_on_message_delete(int(*)(uint32_t)); -void queue_api_on_message_fd_r(int(*)(uint32_t)); -void queue_api_on_envelope_create(int(*)(uint32_t, const char *, size_t, uint64_t *)); -void queue_api_on_envelope_delete(int(*)(uint64_t)); -void queue_api_on_envelope_update(int(*)(uint64_t, const char *, size_t)); -void queue_api_on_envelope_load(int(*)(uint64_t, char *, size_t)); -void queue_api_on_envelope_walk(int(*)(uint64_t *, char *, size_t)); -void queue_api_on_message_walk(int(*)(uint64_t *, char *, size_t, - uint32_t, int *, void **)); -void queue_api_no_chroot(void); -void queue_api_set_chroot(const char *); -void queue_api_set_user(const char *); -int queue_api_dispatch(void); - -/* scheduler */ -void scheduler_api_on_init(int(*)(void)); -void scheduler_api_on_insert(int(*)(struct scheduler_info *)); -void scheduler_api_on_commit(size_t(*)(uint32_t)); -void scheduler_api_on_rollback(size_t(*)(uint32_t)); -void scheduler_api_on_update(int(*)(struct scheduler_info *)); -void scheduler_api_on_delete(int(*)(uint64_t)); -void scheduler_api_on_hold(int(*)(uint64_t, uint64_t)); -void scheduler_api_on_release(int(*)(int, uint64_t, int)); -void scheduler_api_on_batch(int(*)(int, int *, size_t *, uint64_t *, int *)); -void scheduler_api_on_messages(size_t(*)(uint32_t, uint32_t *, size_t)); -void scheduler_api_on_envelopes(size_t(*)(uint64_t, struct evpstate *, size_t)); -void scheduler_api_on_schedule(int(*)(uint64_t)); -void scheduler_api_on_remove(int(*)(uint64_t)); -void scheduler_api_on_suspend(int(*)(uint64_t)); -void scheduler_api_on_resume(int(*)(uint64_t)); -void scheduler_api_no_chroot(void); -void scheduler_api_set_chroot(const char *); -void scheduler_api_set_user(const char *); -int scheduler_api_dispatch(void); - -/* table */ -void table_api_on_update(int(*)(void)); -void table_api_on_check(int(*)(int, struct dict *, const char *)); -void table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t)); -void table_api_on_fetch(int(*)(int, struct dict *, char *, size_t)); -int table_api_dispatch(void); -const char *table_api_get_name(void); - -#endif diff --git a/smtpd/smtpd-defines.h b/smtpd/smtpd-defines.h deleted file mode 100644 index f22a546f..00000000 --- a/smtpd/smtpd-defines.h +++ /dev/null @@ -1,68 +0,0 @@ -/* $OpenBSD: smtpd-defines.h,v 1.12 2020/02/24 16:16:08 millert Exp $ */ - -/* - * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) -#endif - -#define SMTPD_TABLENAME_SIZE (64 + 1) -#define SMTPD_TAG_SIZE (32 + 1) - -/* buffer sizes for email address components */ -#define SMTPD_MAXLOCALPARTSIZE (255 + 1) -#define SMTPD_MAXDOMAINPARTSIZE (255 + 1) -#define SMTPD_MAXMAILADDRSIZE (255 + 1) - -/* buffer size for virtual username (can be email addresses) */ -#define SMTPD_VUSERNAME_SIZE (255 + 1) -#define SMTPD_SUBADDRESS_SIZE (255 + 1) - -#ifndef SMTPD_USER -#define SMTPD_USER "_smtpd" -#endif -#ifndef PATH_CHROOT -#define PATH_CHROOT "/var/empty" -#endif -#ifndef SMTPD_QUEUE_USER -#define SMTPD_QUEUE_USER "_smtpq" -#endif -#ifndef SMTPD_QUEUE_GROUP -#define SMTPD_QUEUE_GROUP "_smtpq" -#endif -#ifndef PATH_SPOOL -#define PATH_SPOOL "/var/spool/smtpd" -#endif - -#ifndef PATH_MAILLOCAL -#define PATH_MAILLOCAL PATH_LIBEXEC "/mail.local" -#endif - -#ifndef PATH_MAKEMAP -#define PATH_MAKEMAP "/usr/sbin/makemap" -#endif - -#define SUBADDRESSING_DELIMITER "+" - - -/* sendmail compat */ - -#define EX_OK 0 -#define EX_NOHOST 68 -#define EX_UNAVAILABLE 69 -#define EX_SOFTWARE 70 -#define EX_TEMPFAIL 75 diff --git a/smtpd/smtpd-filters.7 b/smtpd/smtpd-filters.7 deleted file mode 100644 index 5af7008e..00000000 --- a/smtpd/smtpd-filters.7 +++ /dev/null @@ -1,653 +0,0 @@ -.\" $OpenBSD: smtpd-filters.7,v 1.6 2020/04/25 09:44:02 eric Exp $ -.\" -.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> -.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> -.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" -.Dd $Mdocdate: April 25 2020 $ -.Dt FILTERS 7 -.Os -.Sh NAME -.Nm filters -.Nd filtering API for the -.Xr smtpd 8 -daemon -.Sh DESCRIPTION -The -.Xr smtpd 8 -daemon provides a Simple Mail Transfer Protocol (SMTP) implementation -that allows an ordinary machine to become Mail eXchangers (MX). -Many features that are commonly used by MX, -such as delivery reporting or Spam filtering, -are outside the scope of SMTP and too complex to fit in -.Xr smtpd 8 . -.Pp -Because an MX needs to provide these features, -.Xr smtpd 8 -provides an API to extend its behavior through pluggable -.Nm . -.Pp -At runtime, -.Xr smtpd 8 -can report events to -.Nm -and query what it should answer to these events. -This allows the decision logic to rely on third-party programs. -.Sh DESIGN -The -.Nm -are programs that run as unique standalone processes, -they do not share -.Xr smtpd 8 -memory space. -They are executed by -.Xr smtpd 8 -at startup and expected to run in an infinite loop, -reading events and filtering requests from -.Xr stdin 4 , -writing responses to -.Xr stdout 4 -and logging to -.Xr stderr 4 . -They are not allowed to terminate. -.Pp -Because -.Nm -are standalone programs that communicate with -.Xr smtpd 8 -through -.Xr fd 4 , -they may run as different users than -.Xr smtpd 8 -and may be written in any language. -The -.Nm -must not use blocking I/O, -they must support answering asynchronously to -.Xr smtpd 8 . -.Sh REPORT AND FILTER -The API relies on two streams, -report and filter. -.Pp -The report stream is a one-way stream which allows -.Xr smtpd 8 -to inform -.Nm -in real-time about events that are occurring in the daemon. -The report events do not expect an answer from -.Nm , -it is just meant to provide them with informations. -A filter should be able to replicate the -.Xr smtpd 8 -state for a session by gathering informations coming from report events. -No decision is ever taken by the report stream. -.Pp -The filter stream is a two-way stream which allows -.Xr smtpd 8 -to query -.Nm -about what it should do with a session at a given phase. -The filter requests expects an answer from -.Nm , -.Xr smtpd 8 -will not let the session move forward until then. -A decision must always be taken by the filter stream. -.Pp -It is sometimes possible to rely on filter requests to gather information, -but because a reponse is expected by -.Xr smtpd 8 , -this is more costly than using report events. -The correct pattern for writing filters is to use the report events to -create a local state for a session, -then use filter requests to take decisions based on this state. -The only case when using filter request instead of report events is correct, -is when a decision is required for the filter request and there is no need for -more information than that of the event. -.Sh PROTOCOL -The protocol is straightforward, -it consists of a human-readable line exchanges between -.Nm -and -.Xr smtpd 8 -through -.Xr fd 4 . -.Pp -The protocol begins with a handshake. -First, -.Xr smtpd 8 -provides -.Nm -with general configuration information in the form of key-value lines: -.Bd -literal -offset indent -config|smtpd-version|6.6.1 -config|smtp-session-timeout|300 -config|subsystem|smtp-in -config|ready -.Ed -.Pp -Then, -.Nm -register the stream, -subsystem and event they want to handle: -.Bd -literal -offset indent -register|report|smtp-in|link-connect -register|ready -.Ed -.Pp -Finally, -.Xr smtpd 8 -will emit report events and filter requests, -expecting -.Nm -to react accordingly either by responding or not depending on the stream: -.Bd -literal -offset indent -report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 -report|0.5|1576147242.200225|smtp-in|link-connect|7641dfb3798eb5bf|mail.openbsd.org|pass|199.185.178.25:31205|45.77.67.80:25 -report|0.5|1576148447.982572|smtp-in|link-connect|7641dfc063102cbd|mail.openbsd.org|pass|199.185.178.25:24786|45.77.67.80:25 -.Ed -.Pp -The char -.Dq | -may only appear in the last field of a payload, -in which case it should be considered a regular char and not a separator. -Other fields have strict formatting excluding the possibility of having a -.Dq | . -.Pp -The list of subsystems and events, -as well as the format of requests and reponses, -will be documented in the sections below. -.Sh CONFIGURATION -During the initial handshake, -.Xr smtpd 8 -will emit a serie of configuration keys and values. -The list is meant to be ignored by -.Nm -that do not require it and consumed gracefully by filters that do. -.Pp -There are currently three keys: -.Bd -literal -offset indent -config|smtpd-version|6.6.1 -config|smtp-session-timeout|300 -config|subsystem|smtp-in -.Ed -.Pp -When -.Xr smtpd 8 -has sent all configuration keys it emits the following line: -.Bd -literal -offset indent -config|ready -.Ed -.Sh REPORT EVENTS -There is currently only one subsystem supported in the API: -smtp-in. -.Pp -Each report event is generated by -.Xr smtpd 8 -as a single line similar to the one below: -.Bd -literal -offset indent -report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 -.Ed -.Pp -The format consists of a protocol prefix containing the stream, -the protocol version, -the timestamp, -the subsystem, -the event and the unique session identifier separated by -.Dq | : -.Bd -literal -offset indent -report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00 -.Ed -.Pp -It is followed by a suffix containing the event-specific parameters, -also separated by -.Dq | : -.Bd -literal -offset indent -mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 -.Ed -.Pp -The list of events and event-specific parameters are provided here for smtp-in: -.Bl -tag -width Ds -.It Ic link-connect : Ar rdns fcrdns src dest -This event is generated upon connection. -.Pp -.Ar rdns -contains the reverse DNS hostname for the remote end or an empty string if none. -.Pp -.Ar fcrdns -contains the string -.Dq pass -or -.Dq fail -depending on if the remote end validates FCrDNS. -.Pp -.Ar src -holds either the IP address and port from source address, -in the format -.Dq address:port -or the path to a UNIX socket in the format -.Dq unix:/path . -.Pp -.Ar dest -holds either the IP address and port from destination address, -in the format -.Dq address:port -or the path to a UNIX socket in the format -.Dq unix:/path . -.It Ic link-greeting : Ar hostname -This event is generated upon display of the server banner. -.Pp -.Ar hostname -contains the hostname displayed in the banner. -.It Ic link-identify : Ar method identity -This event is generated upon -.Dq HELO -or -.Dq EHLO -command from the client. -.Pp -.Ar method -contains the string -.Dq HELO -or -.Dq EHLO -indicating the method used by the client. -.Pp -.Ar identity -contains the identity provided by the client. -.It Ic link-tls : Ar tls-string -This event is generated upon successful negotiation of TLS. -.Pp -.Ar tls-string -contains a colon-separated list of TLS properties including the TLS version, -the cipher suite used by the session and the cipher strenght in bits. -.It Ic link-disconnect -This event is generated upon disconnection of the client. -.It Ic link-auth : Ar username result -This event is generated upon authentication attempt of the client. -.Pp -.Ar username -contains the username used for the authentication attempt. -.Pp -.Ar result -contains the string -.Dq pass , -.Dq fail -or -.Dq error -depending on the result of the authentication attempt. -.It Ic tx-reset : Op message-id -This event is generated when a transaction is reset. -.Pp -If reset happend while in a transaction, -.Ar message-id -contains the identifier of the transaction being reset. -.It Ic tx-begin : Ar message-id -This event is generated when a transaction is initiated. -.Pp -.Ar message-id -contains the identifier for the transaction. -.It Ic tx-mail : Ar message-id Ar result address -This event is generated when client emits -.Dq MAIL FROM . -.Pp -.Ar message-id -contains the identifier for the transaction. -.Pp -.Ar result -contains -.Dq ok -if the sender was accepted, -.Dq permfail -if it was rejected -or -.Dq tempfail -if it was rejected for a transient error. -.Pp -.Ar address -contains the e-mail address of the sender. -The address is normalized and sanitized, -the protocol -.Dq < -and -.Dq > -are removed and so are parameters to -.Dq MAIL FROM . -.It Ic tx-rcpt : Ar message-id Ar result address -This event is generated when client emits -.Dq RCPT TO . -.Pp -.Ar message-id -contains the identifier for the transaction. -.Pp -.Ar result -contains -.Dq ok -if the recipient was accepted, -.Dq permfail -if it was rejected -or -.Dq tempfail -if it was rejected for a transient error. -.Pp -.Ar address -contains the e-mail address of the recipient. -The address is normalized and sanitized, -the protocol -.Dq < -and -.Dq > -are removed and so are parameters to -.Dq RCPT TO . -.It Ic tx-envelope : Ar message-id Ar envelope-id -This event is generated when an envelope is accepted. -.Pp -.Ar envelope-id -contains the unique identifier for the envelope. -.It Ic tx-data : Ar message-id Ar result -This event is generated when client has emitted -.Dq DATA . -.Pp -.Ar message-id -contains the unique identifier for the transaction. -.Pp -.Ar result -contains -.Dq ok -if server accepted to process the message, -.Dq permfail -if it has not accepted and -.Dq tempfail -if a transient error is preventing the processing of message. -.It Ic tx-commit : Ar message-id Ar message-size -This event is generated when a transaction has been accepted by the server. -.Pp -.Ar message-id -contains the unique identifier for the SMTP transaction. -.Pp -.Ar message-size -contains the size of the message submitted in the -.Dq DATA -phase of the SMTP transaction. -.It Ic tx-rollback : Ar message-id -This event is generated when a transaction has been rejected by the server. -.Pp -.Ar message-id -contains the unique identifier for the SMTP transaction. -.It Ic protocol-client : Ar command -This event is generated for every command submitted by the client. -It contains the raw command as received by the server. -.Pp -.Ar command -contains the command emitted by the client to the server. -.It Ic protocol-server : Ar response -This event is generated for every response emitted by the server. -It contains the raw response as emitted by the server. -.Pp -.Ar response -contains the response emitted by the server to the client. -.It Ic filter-report : Ar filter-kind Ar name message -This event is generated when a filter emits a report. -.Pp -.Ar filter-kind may be either -.Dq builtin -or -.Dq proc -depending on if the filter is an -.Xr smtpd 8 -builtin filter or a proc filter implementing the API. -.Pp -.Ar name -is the name of the filter that generated the report. -.Pp -.Ar message -is a filter-specific message. -.It Ic filter-response : Ar phase response Op param -This event is generated when a filter responds to a filtering request. -.Pp -.Ar phase -contains the phase name for the request. -The phases are documented in the next section. -.Pp -.Ar response -contains the response of the filter to the request, -it is either one of -.Dq proceed , -.Dq report , -.Dq reject , -.Dq disconnect , -.Dq junk or -.Dq rewrite . -.Pp -If specified, -.Ar param -is the parameter to the response. -.It Ic timeout -This event is generated when a timeout happens for a session. -.El -.Sh FILTER REQUESTS -There is currently only one subsystem supported in the API: -smtp-in. -.Pp -The filter requests allow -.Xr smtpd 8 -to query -.Nm -about what to do with a session at a particular phase. -In addition, -they allow -.Nm -to alter the content of a message by adding, -modifying, -or suppressing lines of input in a way that is similar to what program like -.Xr sed 1 -or -.Xr grep 1 -would do. -.Pp -Each filter request is generated by -.Xr smtpd 8 -as a single line similar to the one below: -.Bd -literal -offset indent -filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 -.Ed -.Pp -The format consists of a protocol prefix containing the stream, -the protocol version, -the timestamp, -the subsystem, -the filtering phase, -the unique session identifier and an opaque token separated by -.Dq | -that the filter should provide in its response: -.Bd -literal -offset indent -filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d -.Ed -.Pp -It is followed by a suffix containing the phase-specific parameters to the -filter request, -also separated by -.Dq | : -.Bd -literal -offset indent -mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 -.Ed -.Pp -Unlike with report events, -.Xr smtpd 8 -expects answers from filter requests and will not allow a session to move -forward before the filter has instructed -.Xr smtpd 8 -what to do with it. -.Pp -For all phases, -excepted -.Dq data-line , -the responses must follow the same construct, -a message type -.Dq filter-result , -followed by the unique session id, -the opaque token, -a decision and optional decision-specific parameters: -.Bd -literal -offset indent -filter-result|7641df9771b4ed00|1ef1c203cc576e5d|proceed -filter-result|7641df9771b4ed00|1ef1c203cc576e5d|reject|550 nope -.Ed -.Pp -The possible decisions to a -.Dq filter-result -message will be described below. -.Pp -For the -.Dq data-line -phase, -.Nm -are fed with a stream of lines corresponding to the message to filter, -and terminated by a single dot: -.Bd -literal -offset indent -filter|0.5|1576146008.006099|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 1 -filter|0.5|1576146008.006103|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 2 -filter|0.5|1576146008.006105|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|. -.Ed -.Pp -They are expected to produce an output stream similarly terminate by a single -dot. -A filter may inject, -suppress, -modify or echo back the lines it receives. -Ultimately, -.Xr smtpd 8 -will assume that the message consists of the output from -.Nm . -.Pp -Note that filters may be chained and the lines that are input into a filter -are the lines that are output from previous filter. -.Pp -The response to -.Dq data-line -requests use their own construct. -A -.Dq filter-dataline -prefix, -followed by the unique session identifier, -the opaque token and the output line as follows: -.Bd -literal -offset indent -filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 1 -filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 2 -filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|. -.Ed -.Pp -The list of events and event-specific parameters are provided here for smtp-in: -.Bl -tag -width Ds -.It Ic connect : Ar rdns fcrdns src dest -This request is emitted after connection, -before the banner is displayed. -.It Ic helo : Ar identity -This request is emitted after the client has emitted -.Dq HELO . -.It Ic ehlo : Ar identity -This request is emitted after the client has emitted -.Dq EHLO . -.It Ic starttls : Ar tls-string -This request is emitted after the client has requested -.Dq STARTTLS . -.It Ic auth : Ar auth -This request is emitted after the client has requested -.Dq AUTH . -.It Ic mail-from : Ar address -This request is emitted after the client has requested -.Dq MAIL FROM . -.It Ic rcpt-to : Ar address -This request is emitted after the client has requested -.Dq RCPT TO . -.It Ic data -This request is emitted after the client has requested -.Dq DATA . -.It Ic data-line : Ar line -This request is emitted for each line of input in the -.Dq DATA -phase. -The lines are raw dot-escaped SMTP DATA input, -terminated with a single dot. -.It Ic commit -This request is emitted after the final single dot is received. -.El -.Pp -For every filtering phase, -excepted -.Dq data-line , -the following decisions may be taken by a filter: -.Bl -tag -width Ds -.It Ic proceed -No action is taken, -session or transaction may be passed to the next filter. -.It Ic junk -The session or transaction is marked as Spam. -.Xr smtpd 8 -will prepend a -.Dq X-Spam -header to the message. -.It Ic reject Ar error -The command is rejected with the message -.Ar error . -The message must be a valid SMTP message including status code, -5xx or 4xx. -.Pp -Messages starting with a 5xx status result in a permanent failure, -those starting with a 4xx status result in a temporary failure. -.Pp -Messages starting with a 421 status will result in a client disconnect. -.It Ic disconnect Ar error -The client is disconnected with the message -.Ar error . -The message must be a valid SMTP message including status code, -5xx or 4xx. -.Pp -Messages starting with a 5xx status result in a permanent failure, -those starting with a 4xx status result in a temporary failure. -.It Ic rewrite Ar parameter -The command parameter is rewritten. -.Pp -This decision allows a filter to perform a rewrite of client-submitted -commands before they are processed by the SMTP engine. -.Ar parameter -is expected to be a valid SMTP parameter for the command. -.It Ic report Ar parameter -Generates a report with -.Ar parameter -for this filter. -.El -.\".Sh EXAMPLES -.\"This example filter written in -.\".Xr sh 1 -.\"will echo back... -.\".Bd -literal -offset indent -.\"XXX -.\".Ed -.\".Pp -.\"This example filter will filter... -.\".Bd -literal -offset indent -.\"XXX -.\".Ed -.\".Pp -.\"Note that libraries may provide a simpler interface to -.\".Nm -.\"that does not require implementing the protocol itself. -.\".Ed -.Sh SEE ALSO -.Xr smtpd 8 -.Sh HISTORY -.Nm -first appeared in -.Ox 6.6 . diff --git a/smtpd/smtpd.8 b/smtpd/smtpd.8 deleted file mode 100644 index e3429f07..00000000 --- a/smtpd/smtpd.8 +++ /dev/null @@ -1,167 +0,0 @@ -.\" $OpenBSD: smtpd.8,v 1.32 2017/01/03 22:11:39 jmc Exp $ -.\" -.\" Copyright (c) 2012, Eric Faurot <eric@openbsd.org> -.\" Copyright (c) 2008, Gilles Chehade <gilles@poolp.org> -.\" Copyright (c) 2008, Pierre-Yves Ritschard <pyr@openbsd.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: January 3 2017 $ -.Dt SMTPD 8 -.Os -.Sh NAME -.Nm smtpd -.Nd Simple Mail Transfer Protocol daemon -.Sh SYNOPSIS -.Nm -.Op Fl dFhnv -.Op Fl D Ar macro Ns = Ns Ar value -.Op Fl f Ar file -.Op Fl P Ar system -.Op Fl T Ar trace -.Sh DESCRIPTION -.Nm -is a Simple Mail Transfer Protocol -.Pq SMTP -daemon which can be used as a machine's primary mail system. -.Nm -can listen on a network interface and handle SMTP -transactions; it can also be fed messages through the standard -.Xr sendmail 8 -interface. -It can relay messages through remote mail transfer agents or store them -locally using either the mbox or maildir format. -This implementation supports SMTP as defined by RFC 5321 as well as several -extensions. -A running -.Nm -can be controlled through -.Xr smtpctl 8 . -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl D Ar macro Ns = Ns Ar value -Define -.Ar macro -to be set to -.Ar value -on the command line. -Overrides the definition of -.Ar macro -in the configuration file. -.It Fl d -Do not daemonize. -If this option is specified, -.Nm -will run in the foreground and log to -.Em stderr . -.It Fl F -Do not daemonize. -If this option is specified, -.Nm -will run in the foreground and log to -.Xr syslogd 8 . -.It Fl f Ar file -Specify an alternative configuration file. -.It Fl h -Display version and usage. -.It Fl n -Configtest mode. -Only check the configuration file for validity. -.It Fl P Ar system -Pause a specific subsystem at startup. -Normal operation can be resumed using -.Xr smtpctl 8 . -This option can be used multiple times. -The accepted values are: -.Pp -.Bl -tag -width "smtpXXX" -compact -.It mda -Do not schedule local deliveries. -.It mta -Do not schedule remote transfers. -.It smtp -Do not listen on SMTP sockets. -.El -.It Fl T Ar trace -Enables real-time tracing at startup. -Normal operation can be resumed using -.Xr smtpctl 8 . -This option can be used multiple times. -The accepted values are: -.Pp -.Bl -bullet -compact -.It -imsg -.It -io -.It -smtp (incoming sessions) -.It -filters -.It -transfer (outgoing sessions) -.It -bounce -.It -scheduler -.It -expand (aliases/virtual/forward expansion) -.It -lookup (user/credentials lookups) -.It -stat -.It -rules (matched by incoming sessions) -.It -mproc -.It -all -.El -.It Fl v -Produce more verbose output. -.El -.Sh FILES -.Bl -tag -width "/etc/mail/smtpd.confXXX" -compact -.It Pa /etc/mail/mailname -Alternate server name to use. -.It Pa /etc/mail/smtpd.conf -Default -.Nm -configuration file. -.It Pa /var/run/smtpd.sock -.Ux Ns -domain -socket used for communication with -.Xr smtpctl 8 . -.It Pa /var/spool/smtpd/ -Spool directories for mail during processing. -.It Pa ~/.forward -User email forwarding information. -.El -.Sh SEE ALSO -.Xr forward 5 , -.Xr smtpd.conf 5 , -.Xr mailwrapper 8 , -.Xr smtpctl 8 -.Sh STANDARDS -.Rs -.%A J. Klensin -.%D October 2008 -.%R RFC 5321 -.%T Simple Mail Transfer Protocol -.Re -.Sh HISTORY -The -.Nm -program first appeared in -.Ox 4.6 . diff --git a/smtpd/smtpd.c b/smtpd/smtpd.c deleted file mode 100644 index f18b1446..00000000 --- a/smtpd/smtpd.c +++ /dev/null @@ -1,2328 +0,0 @@ -/* $OpenBSD: smtpd.c,v 1.333 2020/05/06 16:03:30 millert Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/file.h> /* Needed for flock */ -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <sys/uio.h> -#include <sys/mman.h> - -#ifdef BSD_AUTH -#include <bsd_auth.h> -#endif - -#ifdef USE_PAM -#if defined(HAVE_SECURITY_PAM_APPL_H) -#include <security/pam_appl.h> -#elif defined (HAVE_PAM_PAM_APPL_H) -#include <pam/pam_appl.h> -#endif -#endif - -#ifdef HAVE_CRYPT_H -#include <crypt.h> /* needed for crypt() */ -#endif -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <grp.h> /* needed for setgroups */ -#include <fts.h> -#include <grp.h> -#include <imsg.h> -#include <inttypes.h> -#include <libgen.h> -#ifdef HAVE_LOGIN_CAP_H -#include <login_cap.h> -#endif -#ifdef HAVE_PATHS_H -#include <paths.h> -#endif -#include <poll.h> -#include <pwd.h> -#include <signal.h> -#ifdef HAVE_SHADOW_H -#include <shadow.h> /* needed for getspnam() */ -#endif -#include <stdio.h> -#include <syslog.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <time.h> -#include <unistd.h> -#ifdef HAVE_UTIL_H -#include <util.h> -#endif - -#include <openssl/ssl.h> -#include <openssl/evp.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -extern char *__progname; - -#define SMTPD_MAXARG 32 - -static void parent_imsg(struct mproc *, struct imsg *); -static void usage(void); -static int smtpd(void); -static void parent_shutdown(void); -static void parent_send_config(int, short, void *); -static void parent_send_config_lka(void); -static void parent_send_config_pony(void); -static void parent_send_config_ca(void); -static void parent_sig_handler(int, short, void *); -static void forkmda(struct mproc *, uint64_t, struct deliver *); -static int parent_forward_open(char *, char *, uid_t, gid_t); -static struct child *child_add(pid_t, int, const char *); -static struct mproc *start_child(int, char **, char *); -static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); -static void setup_peers(struct mproc *, struct mproc *); -static void setup_done(struct mproc *); -static void setup_proc(void); -static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); -static int imsg_wait(struct imsgbuf *, struct imsg *, int); - -static void offline_scan(int, short, void *); -static int offline_add(char *, uid_t, gid_t); -static void offline_done(void); -static int offline_enqueue(char *, uid_t, gid_t); - -static void purge_task(void); -static int parent_auth_user(const char *, const char *); -static void load_pki_tree(void); -static void load_pki_keys(void); - -static void fork_filter_processes(void); -static void fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t); - -enum child_type { - CHILD_DAEMON, - CHILD_MDA, - CHILD_PROCESSOR, - CHILD_ENQUEUE_OFFLINE, -}; - -struct child { - pid_t pid; - enum child_type type; - const char *title; - int mda_out; - uint64_t mda_id; - char *path; - char *cause; -}; - -struct offline { - TAILQ_ENTRY(offline) entry; - uid_t uid; - gid_t gid; - char *path; -}; - -#define OFFLINE_READMAX 20 -#define OFFLINE_QUEUEMAX 5 -static size_t offline_running = 0; -TAILQ_HEAD(, offline) offline_q; - -static struct event config_ev; -static struct event offline_ev; -static struct timeval offline_timeout; - -static pid_t purge_pid = -1; - -extern char **environ; -void (*imsg_callback)(struct mproc *, struct imsg *); - -enum smtp_proc_type smtpd_process; - -struct smtpd *env = NULL; - -struct mproc *p_control = NULL; -struct mproc *p_lka = NULL; -struct mproc *p_parent = NULL; -struct mproc *p_queue = NULL; -struct mproc *p_scheduler = NULL; -struct mproc *p_pony = NULL; -struct mproc *p_ca = NULL; - -const char *backend_queue = "fs"; -const char *backend_scheduler = "ramqueue"; -const char *backend_stat = "ram"; - -int profiling = 0; -int debug = 0; -int foreground = 0; -int control_socket = -1; - -struct tree children; - -/* Saved arguments to main(). */ -char **saved_argv; -int saved_argc; - -static void -parent_imsg(struct mproc *p, struct imsg *imsg) -{ - struct forward_req *fwreq; - struct filter_proc *processor; - struct deliver deliver; - struct child *c; - struct msg m; - const void *data; - const char *username, *password, *cause, *procname; - uint64_t reqid; - size_t sz; - void *i; - int fd, n, v, ret; - - if (imsg == NULL) - fatalx("process %s socket closed", p->name); - - switch (imsg->hdr.type) { - case IMSG_LKA_OPEN_FORWARD: - CHECK_IMSG_DATA_SIZE(imsg, sizeof *fwreq); - fwreq = imsg->data; - fd = parent_forward_open(fwreq->user, fwreq->directory, - fwreq->uid, fwreq->gid); - fwreq->status = 0; - if (fd == -1 && errno != ENOENT) { - if (errno == EAGAIN) - fwreq->status = -1; - } - else - fwreq->status = 1; - m_compose(p, IMSG_LKA_OPEN_FORWARD, 0, 0, fd, - fwreq, sizeof *fwreq); - return; - - case IMSG_LKA_AUTHENTICATE: - /* - * If we reached here, it means we want root to lookup - * system user. - */ - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &username); - m_get_string(&m, &password); - m_end(&m); - - ret = parent_auth_user(username, password); - - m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1); - m_add_id(p, reqid); - m_add_int(p, ret); - m_close(p); - return; - - case IMSG_MDA_FORK: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_data(&m, &data, &sz); - m_end(&m); - if (sz != sizeof(deliver)) - fatalx("expected deliver"); - memmove(&deliver, data, sz); - forkmda(p, reqid, &deliver); - return; - - case IMSG_MDA_KILL: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_string(&m, &cause); - m_end(&m); - - i = NULL; - while ((n = tree_iter(&children, &i, NULL, (void**)&c))) - if (c->type == CHILD_MDA && - c->mda_id == reqid && - c->cause == NULL) - break; - if (!n) { - log_debug("debug: smtpd: " - "kill request: proc not found"); - return; - } - - c->cause = xstrdup(cause); - log_debug("debug: smtpd: kill requested for %u: %s", - c->pid, c->cause); - kill(c->pid, SIGTERM); - return; - - case IMSG_CTL_VERBOSE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - log_trace_verbose(v); - return; - - case IMSG_CTL_PROFILE: - m_msg(&m, imsg); - m_get_int(&m, &v); - m_end(&m); - profiling = v; - return; - - case IMSG_LKA_PROCESSOR_ERRFD: - m_msg(&m, imsg); - m_get_string(&m, &procname); - m_end(&m); - - processor = dict_xget(env->sc_filter_processes_dict, procname); - m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd); - m_add_string(p_lka, procname); - m_close(p_lka); - return; - } - - errx(1, "parent_imsg: unexpected %s imsg from %s", - imsg_to_str(imsg->hdr.type), proc_title(p->proc)); -} - -static void -usage(void) -{ - extern char *__progname; - - fprintf(stderr, "usage: %s [-dFhnv] [-D macro=value] " - "[-f file] [-P system] [-T trace]\n", __progname); - exit(1); -} - -static void -parent_shutdown(void) -{ - pid_t pid; - - mproc_clear(p_ca); - mproc_clear(p_pony); - mproc_clear(p_control); - mproc_clear(p_lka); - mproc_clear(p_scheduler); - mproc_clear(p_queue); - - do { - pid = waitpid(WAIT_MYPGRP, NULL, 0); - } while (pid != -1 || (pid == -1 && errno == EINTR)); - - unlink(SMTPD_SOCKET); - - log_info("Exiting"); - exit(0); -} - -static void -parent_send_config(int fd, short event, void *p) -{ - parent_send_config_lka(); - parent_send_config_pony(); - parent_send_config_ca(); - purge_config(PURGE_PKI); -} - -static void -parent_send_config_pony(void) -{ - log_debug("debug: parent_send_config: configuring pony process"); - m_compose(p_pony, IMSG_CONF_START, 0, 0, -1, NULL, 0); - m_compose(p_pony, IMSG_CONF_END, 0, 0, -1, NULL, 0); -} - -void -parent_send_config_lka() -{ - log_debug("debug: parent_send_config_ruleset: reloading"); - m_compose(p_lka, IMSG_CONF_START, 0, 0, -1, NULL, 0); - m_compose(p_lka, IMSG_CONF_END, 0, 0, -1, NULL, 0); -} - -static void -parent_send_config_ca(void) -{ - log_debug("debug: parent_send_config: configuring ca process"); - m_compose(p_ca, IMSG_CONF_START, 0, 0, -1, NULL, 0); - m_compose(p_ca, IMSG_CONF_END, 0, 0, -1, NULL, 0); -} - -static void -parent_sig_handler(int sig, short event, void *p) -{ - struct child *child; - int status, fail; - pid_t pid; - char *cause; - - switch (sig) { - case SIGTERM: - case SIGINT: - log_debug("debug: got signal %d", sig); - parent_shutdown(); - /* NOT REACHED */ - - case SIGCHLD: - do { - int len; - enum mda_resp_status mda_status; - int mda_sysexit; - - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - continue; - - fail = 0; - if (WIFSIGNALED(status)) { - fail = 1; - len = asprintf(&cause, "terminated; signal %d", - WTERMSIG(status)); - mda_status = MDA_TEMPFAIL; - mda_sysexit = 0; - } else if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - fail = 1; - len = asprintf(&cause, - "exited abnormally"); - mda_sysexit = WEXITSTATUS(status); - if (mda_sysexit == EX_OSERR || - mda_sysexit == EX_TEMPFAIL) - mda_status = MDA_TEMPFAIL; - else - mda_status = MDA_PERMFAIL; - } else { - len = asprintf(&cause, "exited okay"); - mda_status = MDA_OK; - mda_sysexit = 0; - } - } else - /* WIFSTOPPED or WIFCONTINUED */ - continue; - - if (len == -1) - fatal("asprintf"); - - if (pid == purge_pid) - purge_pid = -1; - - child = tree_pop(&children, pid); - if (child == NULL) - goto skip; - - switch (child->type) { - case CHILD_PROCESSOR: - if (fail) { - log_warnx("warn: lost processor: %s %s", - child->title, cause); - parent_shutdown(); - } - break; - - case CHILD_DAEMON: - if (fail) - log_warnx("warn: lost child: %s %s", - child->title, cause); - break; - - case CHILD_MDA: - if (WIFSIGNALED(status) && - WTERMSIG(status) == SIGALRM) { - char *tmp; - if (asprintf(&tmp, - "terminated; timeout") != -1) { - free(cause); - cause = tmp; - } - } - else if (child->cause && - WIFSIGNALED(status) && - WTERMSIG(status) == SIGTERM) { - free(cause); - cause = child->cause; - child->cause = NULL; - } - free(child->cause); - log_debug("debug: smtpd: mda process done " - "for session %016"PRIx64 ": %s", - child->mda_id, cause); - - m_create(p_pony, IMSG_MDA_DONE, 0, 0, - child->mda_out); - m_add_id(p_pony, child->mda_id); - m_add_int(p_pony, mda_status); - m_add_int(p_pony, mda_sysexit); - m_add_string(p_pony, cause); - m_close(p_pony); - - break; - - case CHILD_ENQUEUE_OFFLINE: - if (fail) - log_warnx("warn: smtpd: " - "couldn't enqueue offline " - "message %s; smtpctl %s", - child->path, cause); - else - unlink(child->path); - free(child->path); - offline_done(); - break; - - default: - fatalx("smtpd: unexpected child type"); - } - free(child); - skip: - free(cause); - } while (pid > 0 || (pid == -1 && errno == EINTR)); - - break; - default: - fatalx("smtpd: unexpected signal"); - } -} - -int -main(int argc, char *argv[]) -{ - int c, i; - int opts, flags; - const char *conffile = CONF_FILE; - int save_argc = argc; - char **save_argv = argv; - char *rexec = NULL; - struct smtpd *conf; - -#ifndef HAVE___PROGNAME - __progname = ssh_get_progname(argv[0]); -#endif - -#ifndef HAVE_SETPROCTITLE - /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ - saved_argc = argc; - saved_argv = xcalloc(argc + 1, sizeof(*saved_argv)); - for (i = 0; i < argc; i++) - saved_argv[i] = xstrdup(argv[i]); - saved_argv[i] = NULL; - - /* Prepare for later setproctitle emulation */ - compat_init_setproctitle(argc, argv); - argv = saved_argv; - - /* this is to work around GNU getopt + portable setproctitle() fuckery */ - save_argc = saved_argc; - save_argv = saved_argv; -#endif - - if ((conf = config_default()) == NULL) - err(1, NULL); - - env = conf; - - flags = 0; - opts = 0; - debug = 0; - tracing = 0; - - log_init(1, LOG_MAIL); - - TAILQ_INIT(&offline_q); - - while ((c = getopt(argc, argv, "B:dD:hnP:f:FT:vx:")) != -1) { - switch (c) { - case 'B': - if (strstr(optarg, "queue=") == optarg) - backend_queue = strchr(optarg, '=') + 1; - else if (strstr(optarg, "scheduler=") == optarg) - backend_scheduler = strchr(optarg, '=') + 1; - else if (strstr(optarg, "stat=") == optarg) - backend_stat = strchr(optarg, '=') + 1; - else - log_warnx("warn: " - "invalid backend specifier %s", - optarg); - break; - case 'd': - foreground = 1; - foreground_log = 1; - break; - case 'D': - if (cmdline_symset(optarg) < 0) - log_warnx("warn: " - "could not parse macro definition %s", - optarg); - break; - case 'h': - log_info("version: " SMTPD_NAME " " SMTPD_VERSION); - usage(); - break; - case 'n': - debug = 2; - opts |= SMTPD_OPT_NOACTION; - break; - case 'f': - conffile = optarg; - break; - case 'F': - foreground = 1; - break; - - case 'T': - if (!strcmp(optarg, "imsg")) - tracing |= TRACE_IMSG; - else if (!strcmp(optarg, "io")) - tracing |= TRACE_IO; - else if (!strcmp(optarg, "smtp")) - tracing |= TRACE_SMTP; - else if (!strcmp(optarg, "filters")) - tracing |= TRACE_FILTERS; - else if (!strcmp(optarg, "mta") || - !strcmp(optarg, "transfer")) - tracing |= TRACE_MTA; - else if (!strcmp(optarg, "bounce") || - !strcmp(optarg, "bounces")) - tracing |= TRACE_BOUNCE; - else if (!strcmp(optarg, "scheduler")) - tracing |= TRACE_SCHEDULER; - else if (!strcmp(optarg, "lookup")) - tracing |= TRACE_LOOKUP; - else if (!strcmp(optarg, "stat") || - !strcmp(optarg, "stats")) - tracing |= TRACE_STAT; - else if (!strcmp(optarg, "rules")) - tracing |= TRACE_RULES; - else if (!strcmp(optarg, "mproc")) - tracing |= TRACE_MPROC; - else if (!strcmp(optarg, "expand")) - tracing |= TRACE_EXPAND; - else if (!strcmp(optarg, "table") || - !strcmp(optarg, "tables")) - tracing |= TRACE_TABLES; - else if (!strcmp(optarg, "queue")) - tracing |= TRACE_QUEUE; - else if (!strcmp(optarg, "all")) - tracing |= ~TRACE_DEBUG; - else if (!strcmp(optarg, "profstat")) - profiling |= PROFILE_TOSTAT; - else if (!strcmp(optarg, "profile-imsg")) - profiling |= PROFILE_IMSG; - else if (!strcmp(optarg, "profile-queue")) - profiling |= PROFILE_QUEUE; - else - log_warnx("warn: unknown trace flag \"%s\"", - optarg); - break; - case 'P': - if (!strcmp(optarg, "smtp")) - flags |= SMTPD_SMTP_PAUSED; - else if (!strcmp(optarg, "mta")) - flags |= SMTPD_MTA_PAUSED; - else if (!strcmp(optarg, "mda")) - flags |= SMTPD_MDA_PAUSED; - break; - case 'v': - tracing |= TRACE_DEBUG; - break; - case 'x': - rexec = optarg; - break; - default: - usage(); - } - } - - argv += optind; - argc -= optind; - - if (argc || *argv) - usage(); - - env->sc_opts |= opts; - - ssl_init(); - - if (parse_config(conf, conffile, opts)) - exit(1); - - if (RAND_status() != 1) - errx(1, "PRNG is not seeded"); - - if (strlcpy(env->sc_conffile, conffile, PATH_MAX) - >= PATH_MAX) - errx(1, "config file exceeds PATH_MAX"); - - if (env->sc_opts & SMTPD_OPT_NOACTION) { - if (env->sc_queue_key && - crypto_setup(env->sc_queue_key, - strlen(env->sc_queue_key)) == 0) { - fatalx("crypto_setup:" - "invalid key for queue encryption"); - } - load_pki_tree(); - load_pki_keys(); - fprintf(stderr, "configuration OK\n"); - exit(0); - } - - env->sc_flags |= flags; - - /* check for root privileges */ - if (geteuid()) - errx(1, "need root privileges"); - - log_init(foreground_log, LOG_MAIL); - log_trace_verbose(tracing); - load_pki_tree(); - load_pki_keys(); - - log_debug("debug: using \"%s\" queue backend", backend_queue); - log_debug("debug: using \"%s\" scheduler backend", backend_scheduler); - log_debug("debug: using \"%s\" stat backend", backend_stat); - - if (env->sc_hostname[0] == '\0') - errx(1, "machine does not have a hostname set"); - env->sc_uptime = time(NULL); - - if (rexec == NULL) { - smtpd_process = PROC_PARENT; - - if (env->sc_queue_flags & QUEUE_ENCRYPTION) { - if (env->sc_queue_key == NULL) { - char *password; - - password = getpass("queue key: "); - if (password == NULL) - err(1, "getpass"); - - env->sc_queue_key = strdup(password); - explicit_bzero(password, strlen(password)); - if (env->sc_queue_key == NULL) - err(1, "strdup"); - } - else { - char *buf = NULL; - size_t sz = 0; - ssize_t len; - - if (strcasecmp(env->sc_queue_key, "stdin") == 0) { - if ((len = getline(&buf, &sz, stdin)) == -1) - err(1, "getline"); - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - env->sc_queue_key = buf; - } - } - } - - log_info("info: %s %s starting", SMTPD_NAME, SMTPD_VERSION); - - if (!foreground) - if (daemon(0, 0) == -1) - err(1, "failed to daemonize"); - - /* setup all processes */ - - p_ca = start_child(save_argc, save_argv, "ca"); - p_ca->proc = PROC_CA; - - p_control = start_child(save_argc, save_argv, "control"); - p_control->proc = PROC_CONTROL; - - p_lka = start_child(save_argc, save_argv, "lka"); - p_lka->proc = PROC_LKA; - - p_pony = start_child(save_argc, save_argv, "pony"); - p_pony->proc = PROC_PONY; - - p_queue = start_child(save_argc, save_argv, "queue"); - p_queue->proc = PROC_QUEUE; - - p_scheduler = start_child(save_argc, save_argv, "scheduler"); - p_scheduler->proc = PROC_SCHEDULER; - - setup_peers(p_control, p_ca); - setup_peers(p_control, p_lka); - setup_peers(p_control, p_pony); - setup_peers(p_control, p_queue); - setup_peers(p_control, p_scheduler); - setup_peers(p_pony, p_ca); - setup_peers(p_pony, p_lka); - setup_peers(p_pony, p_queue); - setup_peers(p_queue, p_lka); - setup_peers(p_queue, p_scheduler); - - if (env->sc_queue_key) { - if (imsg_compose(&p_queue->imsgbuf, IMSG_SETUP_KEY, 0, - 0, -1, env->sc_queue_key, strlen(env->sc_queue_key) - + 1) == -1) - fatal("imsg_compose"); - if (imsg_flush(&p_queue->imsgbuf) == -1) - fatal("imsg_flush"); - } - - setup_done(p_ca); - setup_done(p_control); - setup_done(p_lka); - setup_done(p_pony); - setup_done(p_queue); - setup_done(p_scheduler); - - log_debug("smtpd: setup done"); - - return smtpd(); - } - - if (!strcmp(rexec, "ca")) { - smtpd_process = PROC_CA; - setup_proc(); - - return ca(); - } - - else if (!strcmp(rexec, "control")) { - smtpd_process = PROC_CONTROL; - setup_proc(); - - /* the control socket ensures that only one smtpd instance is running */ - control_socket = control_create_socket(); - - env->sc_stat = stat_backend_lookup(backend_stat); - if (env->sc_stat == NULL) - errx(1, "could not find stat backend \"%s\"", backend_stat); - - return control(); - } - - else if (!strcmp(rexec, "lka")) { - smtpd_process = PROC_LKA; - setup_proc(); - - return lka(); - } - - else if (!strcmp(rexec, "pony")) { - smtpd_process = PROC_PONY; - setup_proc(); - - return pony(); - } - - else if (!strcmp(rexec, "queue")) { - smtpd_process = PROC_QUEUE; - setup_proc(); - - if (env->sc_queue_flags & QUEUE_COMPRESSION) - env->sc_comp = compress_backend_lookup("gzip"); - - if (!queue_init(backend_queue, 1)) - errx(1, "could not initialize queue backend"); - - return queue(); - } - - else if (!strcmp(rexec, "scheduler")) { - smtpd_process = PROC_SCHEDULER; - setup_proc(); - - for (i = 0; i < MAX_BOUNCE_WARN; i++) { - if (env->sc_bounce_warn[i] == 0) - break; - log_debug("debug: bounce warning after %s", - duration_to_text(env->sc_bounce_warn[i])); - } - - return scheduler(); - } - - fatalx("bad rexec: %s", rexec); - - return (1); -} - -static struct mproc * -start_child(int save_argc, char **save_argv, char *rexec) -{ - struct mproc *p; - char *argv[SMTPD_MAXARG]; - int sp[2], argc = 0; - pid_t pid; - - if (save_argc >= SMTPD_MAXARG - 2) - fatalx("too many arguments"); - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) - fatal("socketpair"); - - io_set_nonblocking(sp[0]); - io_set_nonblocking(sp[1]); - - switch (pid = fork()) { - case -1: - fatal("%s: fork", save_argv[0]); - case 0: - break; - default: - close(sp[0]); - p = calloc(1, sizeof(*p)); - if (p == NULL) - fatal("calloc"); - if((p->name = strdup(rexec)) == NULL) - fatal("strdup"); - mproc_init(p, sp[1]); - p->pid = pid; - p->handler = parent_imsg; - return p; - } - - if (sp[0] != 3) { - if (dup2(sp[0], 3) == -1) - fatal("%s: dup2", rexec); - } else if (fcntl(sp[0], F_SETFD, 0) == -1) - fatal("%s: fcntl", rexec); - - xclosefrom(4); - - for (argc = 0; argc < save_argc; argc++) - argv[argc] = save_argv[argc]; - argv[argc++] = "-x"; - argv[argc++] = rexec; - argv[argc++] = NULL; - - execvp(argv[0], argv); - fatal("%s: execvp", rexec); -} - -static void -setup_peers(struct mproc *a, struct mproc *b) -{ - int sp[2]; - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) - fatal("socketpair"); - - io_set_nonblocking(sp[0]); - io_set_nonblocking(sp[1]); - - if (imsg_compose(&a->imsgbuf, IMSG_SETUP_PEER, b->proc, b->pid, sp[0], - NULL, 0) == -1) - fatal("imsg_compose"); - if (imsg_flush(&a->imsgbuf) == -1) - fatal("imsg_flush"); - - if (imsg_compose(&b->imsgbuf, IMSG_SETUP_PEER, a->proc, a->pid, sp[1], - NULL, 0) == -1) - fatal("imsg_compose"); - if (imsg_flush(&b->imsgbuf) == -1) - fatal("imsg_flush"); -} - -static void -setup_done(struct mproc *p) -{ - struct imsg imsg; - - if (imsg_compose(&p->imsgbuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1) - fatal("imsg_compose"); - if (imsg_flush(&p->imsgbuf) == -1) - fatal("imsg_flush"); - - if (imsg_wait(&p->imsgbuf, &imsg, 10000) == -1) - fatal("imsg_wait"); - - if (imsg.hdr.type != IMSG_SETUP_DONE) - fatalx("expect IMSG_SETUP_DONE"); - - log_debug("setup_done: %s[%d] done", p->name, p->pid); - - imsg_free(&imsg); -} - -static void -setup_proc(void) -{ - struct imsgbuf *ibuf; - struct imsg imsg; - int setup = 1; - - log_procinit(proc_title(smtpd_process)); - - p_parent = calloc(1, sizeof(*p_parent)); - if (p_parent == NULL) - fatal("calloc"); - if((p_parent->name = strdup("parent")) == NULL) - fatal("strdup"); - p_parent->proc = PROC_PARENT; - p_parent->handler = imsg_dispatch; - mproc_init(p_parent, 3); - - ibuf = &p_parent->imsgbuf; - - while (setup) { - if (imsg_wait(ibuf, &imsg, 10000) == -1) - fatal("imsg_wait"); - - switch (imsg.hdr.type) { - case IMSG_SETUP_KEY: - env->sc_queue_key = strdup(imsg.data); - break; - case IMSG_SETUP_PEER: - setup_peer(imsg.hdr.peerid, imsg.hdr.pid, imsg.fd); - break; - case IMSG_SETUP_DONE: - setup = 0; - break; - default: - fatal("bad imsg %d", imsg.hdr.type); - } - imsg_free(&imsg); - } - - if (imsg_compose(ibuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1) - fatal("imsg_compose"); - - if (imsg_flush(ibuf) == -1) - fatal("imsg_flush"); - - log_debug("setup_proc: %s done", proc_title(smtpd_process)); -} - -static struct mproc * -setup_peer(enum smtp_proc_type proc, pid_t pid, int sock) -{ - struct mproc *p, **pp; - - log_debug("setup_peer: %s -> %s[%u] fd=%d", proc_title(smtpd_process), - proc_title(proc), pid, sock); - - if (sock == -1) - fatalx("peer socket not received"); - - switch (proc) { - case PROC_LKA: - pp = &p_lka; - break; - case PROC_QUEUE: - pp = &p_queue; - break; - case PROC_CONTROL: - pp = &p_control; - break; - case PROC_SCHEDULER: - pp = &p_scheduler; - break; - case PROC_PONY: - pp = &p_pony; - break; - case PROC_CA: - pp = &p_ca; - break; - default: - fatalx("unknown peer"); - } - - if (*pp) - fatalx("peer already set"); - - p = calloc(1, sizeof(*p)); - if (p == NULL) - fatal("calloc"); - if((p->name = strdup(proc_title(proc))) == NULL) - fatal("strdup"); - mproc_init(p, sock); - p->pid = pid; - p->proc = proc; - p->handler = imsg_dispatch; - - *pp = p; - - return p; -} - -static int -imsg_wait(struct imsgbuf *ibuf, struct imsg *imsg, int timeout) -{ - struct pollfd pfd[1]; - ssize_t n; - - pfd[0].fd = ibuf->fd; - pfd[0].events = POLLIN; - - while (1) { - if ((n = imsg_get(ibuf, imsg)) == -1) - return -1; - if (n) - return 1; - - n = poll(pfd, 1, timeout); - if (n == -1) - return -1; - if (n == 0) { - errno = ETIMEDOUT; - return -1; - } - - if (((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) || n == 0) - return -1; - } -} - -int -smtpd(void) { - struct event ev_sigint; - struct event ev_sigterm; - struct event ev_sigchld; - struct event ev_sighup; - struct timeval tv; - - imsg_callback = parent_imsg; - - tree_init(&children); - - child_add(p_queue->pid, CHILD_DAEMON, proc_title(PROC_QUEUE)); - child_add(p_control->pid, CHILD_DAEMON, proc_title(PROC_CONTROL)); - child_add(p_lka->pid, CHILD_DAEMON, proc_title(PROC_LKA)); - child_add(p_scheduler->pid, CHILD_DAEMON, proc_title(PROC_SCHEDULER)); - child_add(p_pony->pid, CHILD_DAEMON, proc_title(PROC_PONY)); - child_add(p_ca->pid, CHILD_DAEMON, proc_title(PROC_CA)); - - event_init(); - - signal_set(&ev_sigint, SIGINT, parent_sig_handler, NULL); - signal_set(&ev_sigterm, SIGTERM, parent_sig_handler, NULL); - signal_set(&ev_sigchld, SIGCHLD, parent_sig_handler, NULL); - signal_set(&ev_sighup, SIGHUP, parent_sig_handler, NULL); - signal_add(&ev_sigint, NULL); - signal_add(&ev_sigterm, NULL); - signal_add(&ev_sigchld, NULL); - signal_add(&ev_sighup, NULL); - signal(SIGPIPE, SIG_IGN); - - config_peer(PROC_CONTROL); - config_peer(PROC_LKA); - config_peer(PROC_QUEUE); - config_peer(PROC_CA); - config_peer(PROC_PONY); - - evtimer_set(&config_ev, parent_send_config, NULL); - memset(&tv, 0, sizeof(tv)); - evtimer_add(&config_ev, &tv); - - /* defer offline scanning for a second */ - evtimer_set(&offline_ev, offline_scan, NULL); - offline_timeout.tv_sec = 1; - offline_timeout.tv_usec = 0; - evtimer_add(&offline_ev, &offline_timeout); - - if (pidfile(NULL) < 0) - err(1, "pidfile"); - - fork_filter_processes(); - - purge_task(); - -#if HAVE_PLEDGE - if (pledge("stdio rpath wpath cpath fattr tmppath " - "getpw sendfd proc exec id inet chown unix", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - fatalx("exited event loop"); - - return (0); -} - -static void -load_pki_tree(void) -{ - struct pki *pki; - struct ca *sca; - const char *k; - void *iter_dict; - - log_debug("debug: init ssl-tree"); - iter_dict = NULL; - while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { - log_debug("info: loading pki information for %s", k); - if (pki->pki_cert_file == NULL) - fatalx("load_pki_tree: missing certificate file"); - if (pki->pki_key_file == NULL) - fatalx("load_pki_tree: missing key file"); - - if (!ssl_load_certificate(pki, pki->pki_cert_file)) - fatalx("load_pki_tree: failed to load certificate file"); - } - - log_debug("debug: init ca-tree"); - iter_dict = NULL; - while (dict_iter(env->sc_ca_dict, &iter_dict, &k, (void **)&sca)) { - log_debug("info: loading CA information for %s", k); - if (!ssl_load_cafile(sca, sca->ca_cert_file)) - fatalx("load_pki_tree: failed to load CA file"); - } -} - -void -load_pki_keys(void) -{ - struct pki *pki; - const char *k; - void *iter_dict; - - log_debug("debug: init ssl-tree"); - iter_dict = NULL; - while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { - log_debug("info: loading pki keys for %s", k); - - if (!ssl_load_keyfile(pki, pki->pki_key_file, k)) - fatalx("load_pki_keys: failed to load key file"); - } -} - -int -fork_proc_backend(const char *key, const char *conf, const char *procname) -{ - pid_t pid; - int sp[2]; - char path[PATH_MAX]; - char name[PATH_MAX]; - char *arg; - - if (strlcpy(name, conf, sizeof(name)) >= sizeof(name)) { - log_warnx("warn: %s-proc: conf too long", key); - return (0); - } - - arg = strchr(name, ':'); - if (arg) - *arg++ = '\0'; - - if (snprintf(path, sizeof(path), PATH_LIBEXEC "/%s-%s", key, name) >= - (ssize_t)sizeof(path)) { - log_warn("warn: %s-proc: exec path too long", key); - return (-1); - } - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { - log_warn("warn: %s-proc: socketpair", key); - return (-1); - } - - if ((pid = fork()) == -1) { - log_warn("warn: %s-proc: fork", key); - close(sp[0]); - close(sp[1]); - return (-1); - } - - if (pid == 0) { - /* child process */ - dup2(sp[0], STDIN_FILENO); - closefrom(STDERR_FILENO + 1); - - if (procname == NULL) - procname = name; - - execl(path, procname, arg, (char *)NULL); - err(1, "execl: %s", path); - } - - /* parent process */ - close(sp[0]); - - return (sp[1]); -} - -struct child * -child_add(pid_t pid, int type, const char *title) -{ - struct child *child; - - if ((child = calloc(1, sizeof(*child))) == NULL) - fatal("smtpd: child_add: calloc"); - - child->pid = pid; - child->type = type; - child->title = title; - - tree_xset(&children, pid, child); - - return (child); -} - -static void -purge_task(void) -{ - struct passwd *pw; - DIR *d; - int n; - uid_t uid; - gid_t gid; - - n = 0; - if ((d = opendir(PATH_SPOOL PATH_PURGE))) { - while (readdir(d) != NULL) - n++; - closedir(d); - } else - log_warn("warn: purge_task: opendir"); - - if (n > 2) { - switch (purge_pid = fork()) { - case -1: - log_warn("warn: purge_task: fork"); - break; - case 0: - if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) - fatalx("unknown user " SMTPD_QUEUE_USER); - if (chroot(PATH_SPOOL PATH_PURGE) == -1) - fatal("smtpd: chroot"); - if (chdir("/") == -1) - fatal("smtpd: chdir"); - uid = pw->pw_uid; - gid = pw->pw_gid; - if (setgroups(1, &gid) || - setresgid(gid, gid, gid) || - setresuid(uid, uid, uid)) - fatal("smtpd: cannot drop privileges"); - rmtree("/", 1); - _exit(0); - break; - default: - break; - } - } -} - -static void -fork_filter_processes(void) -{ - const char *name; - void *iter; - const char *fn; - struct filter_config *fc; - struct filter_config *fcs; - struct filter_proc *fp; - size_t i; - - /* For each filter chain, assign the registered subsystem to subfilters */ - iter = NULL; - while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) { - if (fc->chain) { - for (i = 0; i < fc->chain_size; ++i) { - fcs = dict_xget(env->sc_filters_dict, fc->chain[i]); - fcs->filter_subsystem |= fc->filter_subsystem; - } - } - } - - /* For each filter, assign the registered subsystem to underlying proc */ - iter = NULL; - while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) { - if (fc->proc) { - fp = dict_xget(env->sc_filter_processes_dict, fc->proc); - fp->filter_subsystem |= fc->filter_subsystem; - } - } - - iter = NULL; - while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp)) - fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem); -} - -static void -fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems) -{ - pid_t pid; - struct filter_proc *processor; - char buf; - int sp[2], errfd[2]; - struct passwd *pw; - struct group *gr; - char exec[_POSIX_ARG_MAX]; - int execr; - - if (user == NULL) - user = SMTPD_USER; - if ((pw = getpwnam(user)) == NULL) - err(1, "getpwnam"); - - if (group) { - if ((gr = getgrnam(group)) == NULL) - err(1, "getgrnam"); - } - else { - if ((gr = getgrgid(pw->pw_gid)) == NULL) - err(1, "getgrgid"); - } - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) - err(1, "socketpair"); - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errfd) == -1) - err(1, "socketpair"); - - if ((pid = fork()) == -1) - err(1, "fork"); - - /* parent passes the child fd over to lka */ - if (pid > 0) { - processor = dict_xget(env->sc_filter_processes_dict, name); - processor->errfd = errfd[1]; - child_add(pid, CHILD_PROCESSOR, name); - close(sp[0]); - close(errfd[0]); - m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]); - m_add_string(p_lka, name); - m_add_u32(p_lka, (uint32_t)subsystems); - m_close(p_lka); - return; - } - - close(sp[1]); - close(errfd[1]); - dup2(sp[0], STDIN_FILENO); - dup2(sp[0], STDOUT_FILENO); - dup2(errfd[0], STDERR_FILENO); - - if (chroot_path) { - if (chroot(chroot_path) != 0 || chdir("/") != 0) - err(1, "chroot: %s", chroot_path); - } - - if (setgroups(1, &gr->gr_gid) || - setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - err(1, "fork_filter_process: cannot drop privileges"); - - xclosefrom(STDERR_FILENO + 1); - - if (setsid() < 0) - err(1, "setsid"); - if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || - signal(SIGINT, SIG_DFL) == SIG_ERR || - signal(SIGTERM, SIG_DFL) == SIG_ERR || - signal(SIGCHLD, SIG_DFL) == SIG_ERR || - signal(SIGHUP, SIG_DFL) == SIG_ERR) - err(1, "signal"); - - if (command[0] == '/') - execr = snprintf(exec, sizeof(exec), "exec %s", command); - else - execr = snprintf(exec, sizeof(exec), "exec %s/%s", - PATH_LIBEXEC, command); - if (execr >= (int) sizeof(exec)) - errx(1, "%s: exec path too long", name); - - /* - * Wait for lka to acknowledge that it received the fd. - * This prevents a race condition between the filter sending an error - * message, and exiting and lka not being able to log it because of - * SIGCHLD. - * (Ab)use read to determine if the fd is installed; since stderr is - * never going to be read from we can shutdown(2) the write-end in lka. - */ - if (read(STDERR_FILENO, &buf, 1) != 0) - errx(1, "lka didn't properly close write end of error socket"); - if (system(exec) == -1) - err(1, NULL); - - /* there's no successful exit from a processor */ - _exit(1); -} - -static void -forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) -{ - char ebuf[128], sfn[32]; - struct dispatcher *dsp; - struct child *child; - pid_t pid; - int allout, pipefd[2]; - struct passwd *pw; - const char *pw_name; - uid_t pw_uid; - gid_t pw_gid; - const char *pw_dir; - - dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher); - if (dsp->type != DISPATCHER_LOCAL) - fatalx("non-local dispatcher called from forkmda()"); - - log_debug("debug: smtpd: forking mda for session %016"PRIx64 - ": %s as %s", id, deliver->userinfo.username, - dsp->u.local.user ? dsp->u.local.user : deliver->userinfo.username); - - if (dsp->u.local.user) { - if ((pw = getpwnam(dsp->u.local.user)) == NULL) { - (void)snprintf(ebuf, sizeof ebuf, - "delivery user '%s' does not exist", - dsp->u.local.user); - m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); - m_add_id(p_pony, id); - m_add_int(p_pony, MDA_PERMFAIL); - m_add_int(p_pony, EX_NOUSER); - m_add_string(p_pony, ebuf); - m_close(p_pony); - return; - } - pw_name = pw->pw_name; - pw_uid = pw->pw_uid; - pw_gid = pw->pw_gid; - pw_dir = pw->pw_dir; - } - else { - pw_name = deliver->userinfo.username; - pw_uid = deliver->userinfo.uid; - pw_gid = deliver->userinfo.gid; - pw_dir = deliver->userinfo.directory; - } - - if (pw_uid == 0 && deliver->mda_exec[0]) { - pw_name = deliver->userinfo.username; - pw_uid = deliver->userinfo.uid; - pw_gid = deliver->userinfo.gid; - pw_dir = deliver->userinfo.directory; - } - - if (pw_uid == 0 && !dsp->u.local.is_mbox) { - (void)snprintf(ebuf, sizeof ebuf, "not allowed to deliver to: %s", - deliver->userinfo.username); - m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); - m_add_id(p_pony, id); - m_add_int(p_pony, MDA_PERMFAIL); - m_add_int(p_pony, EX_NOPERM); - m_add_string(p_pony, ebuf); - m_close(p_pony); - return; - } - - if (pipe(pipefd) == -1) { - (void)snprintf(ebuf, sizeof ebuf, "pipe: %s", strerror(errno)); - m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); - m_add_id(p_pony, id); - m_add_int(p_pony, MDA_TEMPFAIL); - m_add_int(p_pony, EX_OSERR); - m_add_string(p_pony, ebuf); - m_close(p_pony); - return; - } - - /* prepare file which captures stdout and stderr */ - (void)strlcpy(sfn, "/tmp/smtpd.out.XXXXXXXXXXX", sizeof(sfn)); - allout = mkstemp(sfn); - if (allout == -1) { - (void)snprintf(ebuf, sizeof ebuf, "mkstemp: %s", strerror(errno)); - m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); - m_add_id(p_pony, id); - m_add_int(p_pony, MDA_TEMPFAIL); - m_add_int(p_pony, EX_OSERR); - m_add_string(p_pony, ebuf); - m_close(p_pony); - close(pipefd[0]); - close(pipefd[1]); - return; - } - unlink(sfn); - - pid = fork(); - if (pid == -1) { - (void)snprintf(ebuf, sizeof ebuf, "fork: %s", strerror(errno)); - m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); - m_add_id(p_pony, id); - m_add_int(p_pony, MDA_TEMPFAIL); - m_add_int(p_pony, EX_OSERR); - m_add_string(p_pony, ebuf); - m_close(p_pony); - close(pipefd[0]); - close(pipefd[1]); - close(allout); - return; - } - - /* parent passes the child fd over to mda */ - if (pid > 0) { - child = child_add(pid, CHILD_MDA, NULL); - child->mda_out = allout; - child->mda_id = id; - close(pipefd[0]); - m_create(p, IMSG_MDA_FORK, 0, 0, pipefd[1]); - m_add_id(p, id); - m_close(p); - return; - } - - /* mbox helper, create mailbox before privdrop if it doesn't exist */ - if (dsp->u.local.is_mbox) - mda_mbox_init(deliver); - - if (chdir(pw_dir) == -1 && chdir("/") == -1) - err(1, "chdir"); - if (setgroups(1, &pw_gid) || - setresgid(pw_gid, pw_gid, pw_gid) || - setresuid(pw_uid, pw_uid, pw_uid)) - err(1, "forkmda: cannot drop privileges"); - if (dup2(pipefd[0], STDIN_FILENO) == -1 || - dup2(allout, STDOUT_FILENO) == -1 || - dup2(allout, STDERR_FILENO) == -1) - err(1, "forkmda: dup2"); - closefrom(STDERR_FILENO + 1); - if (setsid() < 0) - err(1, "setsid"); - if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || - signal(SIGINT, SIG_DFL) == SIG_ERR || - signal(SIGTERM, SIG_DFL) == SIG_ERR || - signal(SIGCHLD, SIG_DFL) == SIG_ERR || - signal(SIGHUP, SIG_DFL) == SIG_ERR) - err(1, "signal"); - - /* avoid hangs by setting 5m timeout */ - alarm(300); - - if (dsp->u.local.is_mbox && - dsp->u.local.mda_wrapper == NULL && - deliver->mda_exec[0] == '\0') - mda_mbox(deliver); - else - mda_unpriv(dsp, deliver, pw_name, pw_dir); -} - -static void -offline_scan(int fd, short ev, void *arg) -{ - char *path_argv[2]; - FTS *fts = arg; - FTSENT *e; - int n = 0; - - path_argv[0] = PATH_SPOOL PATH_OFFLINE; - path_argv[1] = NULL; - - if (fts == NULL) { - log_debug("debug: smtpd: scanning offline queue..."); - fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); - if (fts == NULL) { - log_warn("fts_open: %s", path_argv[0]); - return; - } - } - - while ((e = fts_read(fts)) != NULL) { - if (e->fts_info != FTS_F) - continue; - - /* offline files must be at depth 1 */ - if (e->fts_level != 1) - continue; - - /* offline file group must match parent directory group */ - if (e->fts_statp->st_gid != e->fts_parent->fts_statp->st_gid) - continue; - - if (e->fts_statp->st_size == 0) { - if (unlink(e->fts_accpath) == -1) - log_warnx("warn: smtpd: could not unlink %s", e->fts_accpath); - continue; - } - - if (offline_add(e->fts_name, e->fts_statp->st_uid, - e->fts_statp->st_gid)) { - log_warnx("warn: smtpd: " - "could not add offline message %s", e->fts_name); - continue; - } - - if ((n++) == OFFLINE_READMAX) { - evtimer_set(&offline_ev, offline_scan, fts); - offline_timeout.tv_sec = 0; - offline_timeout.tv_usec = 100000; - evtimer_add(&offline_ev, &offline_timeout); - return; - } - } - - log_debug("debug: smtpd: offline scanning done"); - fts_close(fts); -} - -static int -offline_enqueue(char *name, uid_t uid, gid_t gid) -{ - char *path; - struct stat sb; - pid_t pid; - struct child *child; - struct passwd *pw; - int pathlen; - - pathlen = asprintf(&path, "%s/%s", PATH_SPOOL PATH_OFFLINE, name); - if (pathlen == -1) { - log_warnx("warn: smtpd: asprintf"); - return (-1); - } - - if (pathlen >= PATH_MAX) { - log_warnx("warn: smtpd: pathname exceeds PATH_MAX"); - free(path); - return (-1); - } - - log_debug("debug: smtpd: enqueueing offline message %s", path); - - if ((pid = fork()) == -1) { - log_warn("warn: smtpd: fork"); - free(path); - return (-1); - } - - if (pid == 0) { - char *envp[2], *p = NULL, *tmp; - int fd; - FILE *fp; - size_t sz = 0; - ssize_t len; - arglist args; - - closefrom(STDERR_FILENO + 1); - - memset(&args, 0, sizeof(args)); - - if ((fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK)) == -1) { - log_warn("warn: smtpd: open: %s", path); - _exit(1); - } - - if (fstat(fd, &sb) == -1) { - log_warn("warn: smtpd: fstat: %s", path); - _exit(1); - } - - if (!S_ISREG(sb.st_mode)) { - log_warnx("warn: smtpd: file %s (uid %d) not regular", - path, sb.st_uid); - _exit(1); - } - - if (sb.st_nlink != 1) { - log_warnx("warn: smtpd: file %s is hard-link", path); - _exit(1); - } - - if (sb.st_uid != uid) { - log_warnx("warn: smtpd: file %s has bad uid %d", - path, sb.st_uid); - _exit(1); - } - - if (sb.st_gid != gid) { - log_warnx("warn: smtpd: file %s has bad gid %d", - path, sb.st_gid); - _exit(1); - } - - pw = getpwuid(sb.st_uid); - if (pw == NULL) { - log_warnx("warn: smtpd: getpwuid for uid %d failed", - sb.st_uid); - _exit(1); - } - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - _exit(1); - - if ((fp = fdopen(fd, "r")) == NULL) - _exit(1); - - if (chdir(pw->pw_dir) == -1 && chdir("/") == -1) - _exit(1); - - if (setsid() == -1 || - signal(SIGPIPE, SIG_DFL) == SIG_ERR || - dup2(fileno(fp), STDIN_FILENO) == -1) - _exit(1); - - if ((len = getline(&p, &sz, fp)) == -1) - _exit(1); - - if (p[len - 1] != '\n') - _exit(1); - p[len - 1] = '\0'; - - addargs(&args, "%s", "sendmail"); - addargs(&args, "%s", "-S"); - - while ((tmp = strsep(&p, "|")) != NULL) - addargs(&args, "%s", tmp); - - free(p); - if (lseek(fileno(fp), len, SEEK_SET) == -1) - _exit(1); - - envp[0] = "PATH=" _PATH_DEFPATH; - envp[1] = (char *)NULL; - environ = envp; - - execvp(PATH_SMTPCTL, args.list); - _exit(1); - } - - offline_running++; - child = child_add(pid, CHILD_ENQUEUE_OFFLINE, NULL); - child->path = path; - - return (0); -} - -static int -offline_add(char *path, uid_t uid, gid_t gid) -{ - struct offline *q; - - if (offline_running < OFFLINE_QUEUEMAX) - /* skip queue */ - return offline_enqueue(path, uid, gid); - - q = malloc(sizeof(*q) + strlen(path) + 1); - if (q == NULL) - return (-1); - q->uid = uid; - q->gid = gid; - q->path = (char *)q + sizeof(*q); - memmove(q->path, path, strlen(path) + 1); - TAILQ_INSERT_TAIL(&offline_q, q, entry); - - return (0); -} - -static void -offline_done(void) -{ - struct offline *q; - - offline_running--; - - while (offline_running < OFFLINE_QUEUEMAX) { - if ((q = TAILQ_FIRST(&offline_q)) == NULL) - break; /* all done */ - TAILQ_REMOVE(&offline_q, q, entry); - offline_enqueue(q->path, q->uid, q->gid); - free(q); - } -} - -static int -parent_forward_open(char *username, char *directory, uid_t uid, gid_t gid) -{ - char pathname[PATH_MAX]; - int fd; - struct stat sb; - - if (!bsnprintf(pathname, sizeof (pathname), "%s/.forward", - directory)) { - log_warnx("warn: smtpd: %s: pathname too large", pathname); - return -1; - } - - if (stat(directory, &sb) == -1) { - log_warn("warn: smtpd: parent_forward_open: %s", directory); - return -1; - } - if (sb.st_mode & S_ISVTX) { - log_warnx("warn: smtpd: parent_forward_open: %s is sticky", - directory); - errno = EAGAIN; - return -1; - } - - do { - fd = open(pathname, O_RDONLY|O_NOFOLLOW|O_NONBLOCK); - } while (fd == -1 && errno == EINTR); - if (fd == -1) { - if (errno == ENOENT) - return -1; - if (errno == EMFILE || errno == ENFILE || errno == EIO) { - errno = EAGAIN; - return -1; - } - if (errno == ELOOP) - log_warnx("warn: smtpd: parent_forward_open: %s: " - "cannot follow symbolic links", pathname); - else - log_warn("warn: smtpd: parent_forward_open: %s", pathname); - return -1; - } - - if (!secure_file(fd, pathname, directory, uid, 1)) { - log_warnx("warn: smtpd: %s: unsecure file", pathname); - close(fd); - return -1; - } - - return fd; -} - -void -imsg_dispatch(struct mproc *p, struct imsg *imsg) -{ - struct timespec t0, t1, dt; - int msg; - - if (imsg == NULL) { - imsg_callback(p, imsg); - return; - } - - log_imsg(smtpd_process, p->proc, imsg); - - if (profiling & PROFILE_IMSG) - clock_gettime(CLOCK_MONOTONIC, &t0); - - msg = imsg->hdr.type; - imsg_callback(p, imsg); - - if (profiling & PROFILE_IMSG) { - clock_gettime(CLOCK_MONOTONIC, &t1); - timespecsub(&t1, &t0, &dt); - - log_debug("profile-imsg: %s %s %s %d %lld.%09ld", - proc_name(smtpd_process), - proc_name(p->proc), - imsg_to_str(msg), - (int)imsg->hdr.len, - (long long)dt.tv_sec, - dt.tv_nsec); - - if (profiling & PROFILE_TOSTAT) { - char key[STAT_KEY_SIZE]; - /* can't profstat control process yet */ - if (smtpd_process == PROC_CONTROL) - return; - - if (!bsnprintf(key, sizeof key, - "profiling.imsg.%s.%s.%s", - proc_name(smtpd_process), - proc_name(p->proc), - imsg_to_str(msg))) - return; - stat_set(key, stat_timespec(&dt)); - } - } -} - -void -log_imsg(int to, int from, struct imsg *imsg) -{ - - if (to == PROC_CONTROL && imsg->hdr.type == IMSG_STAT_SET) - return; - - if (imsg->fd != -1) - log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu, fd=%d)", - proc_name(to), - proc_name(from), - imsg_to_str(imsg->hdr.type), - imsg->hdr.len - IMSG_HEADER_SIZE, - imsg->fd); - else - log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu)", - proc_name(to), - proc_name(from), - imsg_to_str(imsg->hdr.type), - imsg->hdr.len - IMSG_HEADER_SIZE); -} - -const char * -proc_title(enum smtp_proc_type proc) -{ - switch (proc) { - case PROC_PARENT: - return "[priv]"; - case PROC_LKA: - return "lookup"; - case PROC_QUEUE: - return "queue"; - case PROC_CONTROL: - return "control"; - case PROC_SCHEDULER: - return "scheduler"; - case PROC_PONY: - return "pony express"; - case PROC_CA: - return "klondike"; - case PROC_CLIENT: - return "client"; - case PROC_PROCESSOR: - return "processor"; - } - return "unknown"; -} - -const char * -proc_name(enum smtp_proc_type proc) -{ - switch (proc) { - case PROC_PARENT: - return "parent"; - case PROC_LKA: - return "lka"; - case PROC_QUEUE: - return "queue"; - case PROC_CONTROL: - return "control"; - case PROC_SCHEDULER: - return "scheduler"; - case PROC_PONY: - return "pony"; - case PROC_CA: - return "ca"; - case PROC_CLIENT: - return "client-proc"; - default: - return "unknown"; - } -} - -#define CASE(x) case x : return #x - -const char * -imsg_to_str(int type) -{ - static char buf[32]; - - switch (type) { - CASE(IMSG_NONE); - - CASE(IMSG_CTL_OK); - CASE(IMSG_CTL_FAIL); - - CASE(IMSG_CTL_GET_DIGEST); - CASE(IMSG_CTL_GET_STATS); - CASE(IMSG_CTL_LIST_MESSAGES); - CASE(IMSG_CTL_LIST_ENVELOPES); - CASE(IMSG_CTL_MTA_SHOW_HOSTS); - CASE(IMSG_CTL_MTA_SHOW_RELAYS); - CASE(IMSG_CTL_MTA_SHOW_ROUTES); - CASE(IMSG_CTL_MTA_SHOW_HOSTSTATS); - CASE(IMSG_CTL_MTA_BLOCK); - CASE(IMSG_CTL_MTA_UNBLOCK); - CASE(IMSG_CTL_MTA_SHOW_BLOCK); - CASE(IMSG_CTL_PAUSE_EVP); - CASE(IMSG_CTL_PAUSE_MDA); - CASE(IMSG_CTL_PAUSE_MTA); - CASE(IMSG_CTL_PAUSE_SMTP); - CASE(IMSG_CTL_PROFILE); - CASE(IMSG_CTL_PROFILE_DISABLE); - CASE(IMSG_CTL_PROFILE_ENABLE); - CASE(IMSG_CTL_RESUME_EVP); - CASE(IMSG_CTL_RESUME_MDA); - CASE(IMSG_CTL_RESUME_MTA); - CASE(IMSG_CTL_RESUME_SMTP); - CASE(IMSG_CTL_RESUME_ROUTE); - CASE(IMSG_CTL_REMOVE); - CASE(IMSG_CTL_SCHEDULE); - CASE(IMSG_CTL_SHOW_STATUS); - CASE(IMSG_CTL_TRACE_DISABLE); - CASE(IMSG_CTL_TRACE_ENABLE); - CASE(IMSG_CTL_UPDATE_TABLE); - CASE(IMSG_CTL_VERBOSE); - CASE(IMSG_CTL_DISCOVER_EVPID); - CASE(IMSG_CTL_DISCOVER_MSGID); - - CASE(IMSG_CTL_SMTP_SESSION); - - CASE(IMSG_GETADDRINFO); - CASE(IMSG_GETADDRINFO_END); - CASE(IMSG_GETNAMEINFO); - CASE(IMSG_RES_QUERY); - - CASE(IMSG_CERT_INIT); - CASE(IMSG_CERT_CERTIFICATE); - CASE(IMSG_CERT_VERIFY); - - CASE(IMSG_SETUP_KEY); - CASE(IMSG_SETUP_PEER); - CASE(IMSG_SETUP_DONE); - - CASE(IMSG_CONF_START); - CASE(IMSG_CONF_END); - - CASE(IMSG_STAT_INCREMENT); - CASE(IMSG_STAT_DECREMENT); - CASE(IMSG_STAT_SET); - - CASE(IMSG_LKA_AUTHENTICATE); - CASE(IMSG_LKA_OPEN_FORWARD); - CASE(IMSG_LKA_ENVELOPE_SUBMIT); - CASE(IMSG_LKA_ENVELOPE_COMMIT); - - CASE(IMSG_QUEUE_DELIVER); - CASE(IMSG_QUEUE_DELIVERY_OK); - CASE(IMSG_QUEUE_DELIVERY_TEMPFAIL); - CASE(IMSG_QUEUE_DELIVERY_PERMFAIL); - CASE(IMSG_QUEUE_DELIVERY_LOOP); - CASE(IMSG_QUEUE_DISCOVER_EVPID); - CASE(IMSG_QUEUE_DISCOVER_MSGID); - CASE(IMSG_QUEUE_ENVELOPE_ACK); - CASE(IMSG_QUEUE_ENVELOPE_COMMIT); - CASE(IMSG_QUEUE_ENVELOPE_REMOVE); - CASE(IMSG_QUEUE_ENVELOPE_SCHEDULE); - CASE(IMSG_QUEUE_ENVELOPE_SUBMIT); - CASE(IMSG_QUEUE_HOLDQ_HOLD); - CASE(IMSG_QUEUE_HOLDQ_RELEASE); - CASE(IMSG_QUEUE_MESSAGE_COMMIT); - CASE(IMSG_QUEUE_MESSAGE_ROLLBACK); - CASE(IMSG_QUEUE_SMTP_SESSION); - CASE(IMSG_QUEUE_TRANSFER); - - CASE(IMSG_MDA_DELIVERY_OK); - CASE(IMSG_MDA_DELIVERY_TEMPFAIL); - CASE(IMSG_MDA_DELIVERY_PERMFAIL); - CASE(IMSG_MDA_DELIVERY_LOOP); - CASE(IMSG_MDA_DELIVERY_HOLD); - CASE(IMSG_MDA_DONE); - CASE(IMSG_MDA_FORK); - CASE(IMSG_MDA_HOLDQ_RELEASE); - CASE(IMSG_MDA_LOOKUP_USERINFO); - CASE(IMSG_MDA_KILL); - CASE(IMSG_MDA_OPEN_MESSAGE); - - CASE(IMSG_MTA_DELIVERY_OK); - CASE(IMSG_MTA_DELIVERY_TEMPFAIL); - CASE(IMSG_MTA_DELIVERY_PERMFAIL); - CASE(IMSG_MTA_DELIVERY_LOOP); - CASE(IMSG_MTA_DELIVERY_HOLD); - CASE(IMSG_MTA_DNS_HOST); - CASE(IMSG_MTA_DNS_HOST_END); - CASE(IMSG_MTA_DNS_MX); - CASE(IMSG_MTA_DNS_MX_PREFERENCE); - CASE(IMSG_MTA_HOLDQ_RELEASE); - CASE(IMSG_MTA_LOOKUP_CREDENTIALS); - CASE(IMSG_MTA_LOOKUP_SOURCE); - CASE(IMSG_MTA_LOOKUP_HELO); - CASE(IMSG_MTA_LOOKUP_SMARTHOST); - CASE(IMSG_MTA_OPEN_MESSAGE); - CASE(IMSG_MTA_SCHEDULE); - - CASE(IMSG_SCHED_ENVELOPE_BOUNCE); - CASE(IMSG_SCHED_ENVELOPE_DELIVER); - CASE(IMSG_SCHED_ENVELOPE_EXPIRE); - CASE(IMSG_SCHED_ENVELOPE_INJECT); - CASE(IMSG_SCHED_ENVELOPE_REMOVE); - CASE(IMSG_SCHED_ENVELOPE_TRANSFER); - - CASE(IMSG_SMTP_AUTHENTICATE); - CASE(IMSG_SMTP_MESSAGE_COMMIT); - CASE(IMSG_SMTP_MESSAGE_CREATE); - CASE(IMSG_SMTP_MESSAGE_ROLLBACK); - CASE(IMSG_SMTP_MESSAGE_OPEN); - CASE(IMSG_SMTP_CHECK_SENDER); - CASE(IMSG_SMTP_EXPAND_RCPT); - CASE(IMSG_SMTP_LOOKUP_HELO); - - CASE(IMSG_SMTP_REQ_CONNECT); - CASE(IMSG_SMTP_REQ_HELO); - CASE(IMSG_SMTP_REQ_MAIL); - CASE(IMSG_SMTP_REQ_RCPT); - CASE(IMSG_SMTP_REQ_DATA); - CASE(IMSG_SMTP_REQ_EOM); - CASE(IMSG_SMTP_EVENT_RSET); - CASE(IMSG_SMTP_EVENT_COMMIT); - CASE(IMSG_SMTP_EVENT_ROLLBACK); - CASE(IMSG_SMTP_EVENT_DISCONNECT); - - CASE(IMSG_LKA_PROCESSOR_FORK); - CASE(IMSG_LKA_PROCESSOR_ERRFD); - - CASE(IMSG_REPORT_SMTP_LINK_CONNECT); - CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT); - CASE(IMSG_REPORT_SMTP_LINK_TLS); - CASE(IMSG_REPORT_SMTP_LINK_GREETING); - CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY); - CASE(IMSG_REPORT_SMTP_LINK_AUTH); - - CASE(IMSG_REPORT_SMTP_TX_RESET); - CASE(IMSG_REPORT_SMTP_TX_BEGIN); - CASE(IMSG_REPORT_SMTP_TX_ENVELOPE); - CASE(IMSG_REPORT_SMTP_TX_COMMIT); - CASE(IMSG_REPORT_SMTP_TX_ROLLBACK); - - CASE(IMSG_REPORT_SMTP_PROTOCOL_CLIENT); - CASE(IMSG_REPORT_SMTP_PROTOCOL_SERVER); - - CASE(IMSG_FILTER_SMTP_BEGIN); - CASE(IMSG_FILTER_SMTP_END); - CASE(IMSG_FILTER_SMTP_PROTOCOL); - CASE(IMSG_FILTER_SMTP_DATA_BEGIN); - CASE(IMSG_FILTER_SMTP_DATA_END); - - CASE(IMSG_CA_RSA_PRIVENC); - CASE(IMSG_CA_RSA_PRIVDEC); - CASE(IMSG_CA_ECDSA_SIGN); - default: - (void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type); - - return buf; - } -} - -#ifdef BSD_AUTH -int -parent_auth_bsd(const char *username, const char *password) -{ - char user[LOGIN_NAME_MAX]; - char pass[LINE_MAX]; - int ret; - - (void)strlcpy(user, username, sizeof(user)); - (void)strlcpy(pass, password, sizeof(pass)); - - ret = auth_userokay(user, NULL, "auth-smtp", pass); - if (ret) - return LKA_OK; - return LKA_PERMFAIL; -} -#endif - -#ifdef USE_PAM -int -pam_conv_password(int num_msg, const struct pam_message **msg, - struct pam_response **respp, void *password) -{ - struct pam_response *response; - - if (num_msg != 1) - return PAM_CONV_ERR; - - response = calloc(1, sizeof(struct pam_response)); - if (response == NULL || (response->resp = strdup(password)) == NULL) { - free(response); - return PAM_BUF_ERR; - } - - *respp = response; - return PAM_SUCCESS; -} -int -parent_auth_pam(const char *username, const char *password) -{ - int rc; - pam_handle_t *pamh = NULL; - struct pam_conv conv = { pam_conv_password, (char *)password }; - - if ((rc = pam_start(USE_PAM_SERVICE, username, &conv, &pamh)) != PAM_SUCCESS) - goto end; - if ((rc = pam_authenticate(pamh, 0)) != PAM_SUCCESS) - goto end; - if ((rc = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) - goto end; - -end: - pam_end(pamh, rc); - - switch (rc) { - case PAM_SUCCESS: - return LKA_OK; - case PAM_SYSTEM_ERR: - case PAM_ABORT: - case PAM_AUTHINFO_UNAVAIL: - return LKA_TEMPFAIL; - default: - return LKA_PERMFAIL; - } -} -#endif - -#ifdef HAVE_GETSPNAM -int -parent_auth_getspnam(const char *username, const char *password) -{ - struct spwd *pw; - char *ep; - - errno = 0; - do { - pw = getspnam(username); - } while (pw == NULL && errno == EINTR); - - if (pw == NULL) { - if (errno) - return LKA_TEMPFAIL; - return LKA_PERMFAIL; - } - - if ((ep = crypt(password, pw->sp_pwdp)) == NULL) - return LKA_PERMFAIL; - - if (strcmp(pw->sp_pwdp, ep) == 0) - return LKA_OK; - - return LKA_PERMFAIL; -} -#endif - -int -parent_auth_pwd(const char *username, const char *password) -{ - struct passwd *pw; - char *ep; - - errno = 0; - do { - pw = getpwnam(username); - } while (pw == NULL && errno == EINTR); - - if (pw == NULL) { - if (errno) - return LKA_TEMPFAIL; - return LKA_PERMFAIL; - } - - if ((ep = crypt(password, pw->pw_passwd)) == NULL) - return LKA_PERMFAIL; - - if (strcmp(pw->pw_passwd, ep) == 0) - return LKA_OK; - - return LKA_PERMFAIL; -} - -int -parent_auth_user(const char *username, const char *password) -{ -#if defined(BSD_AUTH) - return (parent_auth_bsd(username, password)); -#elif defined(USE_PAM) - return (parent_auth_pam(username, password)); -#elif defined(HAVE_GETSPNAM) - return (parent_auth_getspnam(username, password)); -#else - return (parent_auth_pwd(username, password)); -#endif -} diff --git a/smtpd/smtpd.conf b/smtpd/smtpd.conf deleted file mode 100644 index a7ba6c64..00000000 --- a/smtpd/smtpd.conf +++ /dev/null @@ -1,19 +0,0 @@ -# $OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $ - -# This is the smtpd server system-wide configuration file. -# See smtpd.conf(5) for more information. - -table aliases file:/etc/mail/aliases - -# To accept external mail, replace with: listen on all -# -listen on localhost - -action "local" maildir alias <aliases> -action "relay" relay - -# Uncomment the following to accept external mail for domain "example.org" -# -# match from any for domain "example.org" action "local" -match for local action "local" -match from local for any action "relay" diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5 deleted file mode 100644 index c543c662..00000000 --- a/smtpd/smtpd.conf.5 +++ /dev/null @@ -1,1240 +0,0 @@ -.\" $OpenBSD: smtpd.conf.5,v 1.250 2020/04/25 09:20:38 eric Exp $ -.\" -.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> -.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> -.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" -.Dd $Mdocdate: April 25 2020 $ -.Dt SMTPD.CONF 5 -.Os -.Sh NAME -.Nm smtpd.conf -.Nd Simple Mail Transfer Protocol daemon configuration file -.Sh DESCRIPTION -.Nm -is the configuration file for the mail daemon -.Xr smtpd 8 . -.Pp -When mail arrives, -each -.Dq RCPT TO: -command generates a mail envelope. -If an envelope matches -any of a pre-designated set of criteria -(using the -.Ic match -directive), -the message is accepted for delivery. -A copy of the message, as well as its associated envelopes, -is saved in the mail queue and later dispatched -according to an associated set of actions -(using the -.Ic action -directive). -If an envelope does not match any options, -it is rejected. -The match rules are evaluated sequentially, -with the first match winning. -.Pp -The format of the configuration file is fairly flexible. -The current line can be extended over multiple lines using a backslash -.Pq Sq \e . -Comments can be put anywhere in the file using a hash mark -.Pq Sq # , -and extend to the end of the current line. -Care should be taken when commenting out multi-line text: -the comment is effective until the end of the entire block. -Argument names not beginning with a letter, digit, or underscore, -as well as reserved words -(such as -.Ic listen , -.Ic match , -and -.Cm port ) , -must be quoted. -Arguments containing whitespace should be surrounded by double quotes -.Pq \&" . -.Pp -Macros can be defined that are later expanded in context. -Macro names must start with a letter, digit, or underscore, -and may contain any of those characters, -but may not be reserved words. -Macros are not expanded inside quotes. -For example: -.Bd -literal -offset indent -lan_addr = "192.168.0.1" -listen on $lan_addr -listen on $lan_addr tls auth -.Ed -.Pp -The syntax of -.Nm -is described below. -.Bl -tag -width Ds -.It Ic action Ar name method Op Ar options -When the queue runner processes an envelope from the mail queue, -it carries out the -.Ic action -.Ar name , -selected by the -.Ic match No ... Cm action -directive when the message was received. -The -.Ic action -directive provides configuration data for delivery attempts. -Required lookups are performed at the time of each delivery attempt. -Consequently, changing an -.Ic action -directive or the files it references and restarting the -.Xr smtpd 8 -daemon causes the changes to take effect for subsequent delivery -attempts for the respective dispatcher -.Ar name , -even for messages that were already stuck in the queue -prior to the configuration changes. -.Pp -The delivery -.Ar method -parameter may be one of the following: -.Bl -tag -width Ds -.It Cm expand-only -Only accept the message if a delivery method was specified -in an aliases or -.Pa .forward -file. -.It Cm forward-only -Only accept the message if the recipient results in a remote address -after the processing of aliases or forward file. -.It Cm lmtp Ar destination Op Ar rcpt-to -Deliver the message to an LMTP server at -.Ar destination . -The location may be expressed as host:port or as a UNIX socket. -.Pp -Optionally, -.Ar rcpt-to -might be specified to use the -recipient email address (after expansion) instead of the -local user in the LMTP session as RCPT TO. -.It Cm maildir Op Ar pathname Op Cm junk -Deliver the message to the maildir in -.Ar pathname -if specified, or by default to -.Pa ~/Maildir . -.Pp -The -.Ar pathname -may contain format specifiers that are expanded before use -.Pq see Sx FORMAT SPECIFIERS . -.Pp -If the -.Cm junk -argument is provided, the message will be moved to the -.Ql Junk -folder if it contains a positive -.Ql X-Spam -header. -This folder will be created under -.Ar pathname -if it does not yet exist. -.It Cm mbox -Deliver the message to the user's mbox with -.Xr mail.local 8 . -.It Cm mda Ar command -Delegate the delivery to a -.Ar command -that receives the message on its standard input. -.Pp -The -.Ar command -may contain format specifiers that are expanded before use -.Pq see Sx FORMAT SPECIFIERS . -.It Cm relay -Relay the message to another SMTP server. -.El -.Pp -The local delivery methods support additional options: -.Bl -tag -width Ds -.It Cm alias Pf < Ar table Ns > -Use the mapping -.Ar table -for -.Xr aliases 5 -expansion. -.It Xo -.Cm ttl -.Sm off -.Ar n -.Brq Cm s | m | h | d -.Sm on -.Xc -Specify how long a message may remain in the queue. -.It Cm user Ar username -Specify the -.Ar username -for performing the delivery, to be looked up with -.Xr getpwnam 3 . -.Pp -This is used for virtual hosting where a single username -is in charge of handling delivery for all virtual users. -.Pp -This option is not usable with the -.Cm mbox -delivery method. -.It Cm userbase Pf < Ar table Ns > -Use the mapping -.Ar table -for user lookups instead of the -.Xr getpwnam 3 -function. -.Pp -The -.Cm userbase -does not apply for the -.Cm user -option. -.It Cm virtual Pf < Ar table Ns > -Use the mapping -.Ar table -for virtual expansion. -The aliasing table format is described in -.Xr table 5 . -.It Cm wrapper Ar name -Use the wrapper specified in -.Cm mda wrapper . -.El -.Pp -The relay delivery methods also support additional options: -.Bl -tag -width Ds -.It Cm backup -Operate as a backup mail exchanger delivering messages to any mail exchanger -with higher priority. -.It Cm backup mx Ar name -Operate as a backup mail exchanger delivering messages to any mail exchanger -with higher priority than mail exchanger identified as -.Ar name . -.It Cm helo Ar heloname -Advertise -.Ar heloname -as the hostname to other mail exchangers during the HELO phase. -.It Cm helo-src Pf < Ar table Ns > -Use the mapping -.Ar table -to look up a hostname matching the source address, -to advertise during the HELO phase. -.It Cm domain Pf < Ar domains Ns > -Do not perform MX lookups but look up destination domain in -.Ar domains -and use matching relay url as relay host. -.It Cm host Ar relay-url -Do not perform MX lookups but relay messages to the relay host described by -.Ar relay-url . -The format for -.Ar relay-url -is -.Sm off -.Op Ar proto No :// Op Ar label No @ -.Ar host Op : Ar port . -.Sm on -The following protocols are available: -.Pp -.Bl -tag -width "smtp+notls" -compact -.It smtp -Normal SMTP session with opportunistic STARTTLS -(the default). -.It smtp+tls -Normal SMTP session with mandatory STARTTLS. -.It smtp+notls -Plain text SMTP session without TLS. -.It lmtp -LMTP session. -.Ar port -is required. -.It smtps -SMTP session with forced TLS on connection, default port is 465. -.El -Unless noted, -.Ar port -defaults to 25. -.Pp -The -.Ar label -corresponds to an entry in a credentials table, -as documented in -.Xr table 5 . -It is used with the -.Dq smtp+tls -and -.Dq smtps -protocols for authentication. -Server certificates for those protocols are verified by default. -.It Cm srs -When relaying a mail resulting from a forward, -use the Sender Rewriting Scheme to rewrite sender address. -.It Cm tls Op Cm no-verify -Require TLS to be used when relaying, using mandatory STARTTLS by default. -When used with a smarthost, the protocol must not be -.Dq smtp+notls:// . -If -.Cm no-verify -is specified, do not require a valid certificate. -.It Cm auth Pf < Ar table Ns > -Use the mapping -.Ar table -for connecting to -.Ar relay-url -using credentials. -This option is usable only with -.Cm host -option. -The credential table format is described in -.Xr table 5 . -.It Cm mail-from Ar mailaddr -Use -.Ar mailaddr -as the MAIL FROM address within the SMTP transaction. -.It Cm src Ar sourceaddr | Pf < Ar sourceaddr Ns > -Use the string or list table -.Ar sourceaddr -for the source IP address, -which is useful on machines with multiple interfaces. -If the list contains more than one address, all of them are used -in such a way that traffic is routed as efficiently as possible. -.El -.It Ic bounce Cm warn-interval Ar delay Op , Ar delay ... -Send warning messages to the envelope sender when temporary delivery -failures cause a message to remain on the queue for longer than -.Ar delay . -Each -.Ar delay -parameter consists of a positive decimal integer and a unit -.Cm s , m , h , -or -.Cm d . -At most four -.Ar delay -parameters can be specified. -The default is -.Qq Ic bounce Cm warn-interval No 4h , -sending a single warning after four hours. -.It Ic ca Ar caname Cm cert Ar cafile -Associate the Certificate Authority (CA) certificate file -.Ar cafile -with host -.Ar caname , -and use that file as the CA certificate for that host. -.Ar caname -is the server's name, -derived from the default hostname -or set using either -.Pa /etc/mail/mailname -or using the -.Ic hostname -directive. -.It Ic filter Ar chain-name Ic chain Brq Ar filter-name Op , Ar ... -Register a chain of filters -.Ar chain-name , -consisting of the filters listed from -.Ar filter-name . -Filters part of a filter chain are executed in order of declaration for -each phase that they are registered for. -A filter chain may be used in place of a filter for any directive but -filter chains themselves. -.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions decision -Register a filter -.Ar filter-name . -A -.Ar decision -about what to do with the mail is taken at phase -.Ar phase-name -when matching -.Ar conditions . -Phases, matching conditions, and decisions are described in -.Sx MAIL FILTERING , -below. -.It Ic filter Ar filter-name Ic proc Ar proc-name -Register -.Qq proc -filter -.Ar filter-name -backed by the -.Ar proc-name -process. -.It Ic filter Ar filter-name Ic proc-exec Ar command -Register and execute -.Qq proc -filter -.Ar filter-name -from -.Ar command . -If -.Ar command -starts with a slash it is executed with an absolute path, -else it will be run from -.Dq /usr/local/libexec/smtpd/ . -.It Ic include Qq Ar pathname -Replace this directive with the content of the additional configuration -file at the absolute -.Ar pathname . -.It Ic listen on Ar interface Oo Ar family Oc Op Ar options -Listen on the -.Ar interface -for incoming connections, using the same syntax as for -.Xr ifconfig 8 . -The -.Ar interface -parameter may also be an interface group, an IP address, or a domain name. -Listening can optionally be restricted to a specific address -.Ar family , -which can be either -.Cm inet4 -or -.Cm inet6 . -.Pp -The -.Ar options -are as follows: -.Bl -tag -width Ds -.It Cm auth Op Pf < Ar authtable Ns > -Support SMTPAUTH: clients may only start SMTP transactions -after successful authentication. -Users are authenticated against either their own normal login credentials -or a credentials table -.Ar authtable , -the format of which is described in -.Xr table 5 . -.It Cm auth-optional Op Pf < Ar authtable Ns > -Support SMTPAUTH optionally: -clients need not authenticate, but may do so. -This allows a -.Ic listen on -directive to both accept incoming mail from untrusted senders -and permit outgoing mail from authenticated users -(using -.Cm match auth ) . -It can be used in situations where it is not possible to listen on a separate port -(usually the submission port, 587) -for users to authenticate. -.It Ic ca Ar caname -For secure connections, -use the CA certificate associated with -.Ar caname -(declared in a -.Ic ca -directive) -as the CA certificate when verifying client certificates. -.It Ic filter Ar name -Apply filter -.Ar name -on connections handled by this listener. -.It Cm hostname Ar hostname -Use -.Ar hostname -in the greeting banner instead of the default server name. -.It Cm hostnames Pf < Ar names Ns > -Override the server name for specific addresses. -The -.Ar names -table contains a mapping of IP addresses to hostnames. -If the address on which the connection arrives appears in the mapping, -the associated hostname is used. -.It Cm mask-src -Omit the -.Sy from -part when prepending -.Dq Received -headers. -.It Cm no-dsn -Disable the DSN (Delivery Status Notification) extension. -.It Cm pki Ar pkiname -For secure connections, -use the certificate associated with -.Ar pkiname -(declared in a -.Ic pki -directive) -to prove a mail server's identity. -.It Cm port Op Ar port -Listen on the given -.Ar port -instead of the default port 25. -.It Cm proxy-v2 -Support the PROXYv2 protocol, -rewriting appropriately source address received from proxy. -.It Cm received-auth -In -.Dq Received -headers, report whether the session was authenticated -and by which local user. -.It Cm senders Pf < Ar users Ns > Op Cm masquerade -Look up the authenticated user in the -.Ar users -mapping table to find the email addresses that user is allowed -to submit mail as. -In addition, if the -.Cm masquerade -option is provided, -the From header is rewritten -to match the sender provided in the SMTP session. -.It Cm smtps -Support SMTPS, by default on port 465. -Mutually exclusive with -.Cm tls . -.It Cm tag Ar tag -Clients connecting to the listener are tagged with the given -.Ar tag . -.It Cm tls -Support STARTTLS, by default on port 25. -Mutually exclusive with -.Cm smtps . -.It Cm tls-require Op Cm verify -Like -.Cm tls , -but force clients to establish a secure connection -before being allowed to start an SMTP transaction. -With the -.Cm verify -option, clients must also provide a valid certificate -to establish an SMTP session. -.El -.It Ic listen on Cm socket Op Ar options -Listen for incoming SMTP connections on the Unix domain socket -.Pa /var/run/smtpd.sock . -This is done by default, even if the directive is absent. -.Pp -The -.Ar options -are as follows: -.Bl -tag -width Ds -.It Ic filter Ar name -Apply filter -.Ar name -on connections handled by this listener. -.It Cm mask-src -Omit the -.Sy from -part when prepending -.Dq Received -headers. -.It Cm tag Ar tag -Clients connecting to the listener are tagged with the given -.Ar tag . -.El -.It Ic match Ar options Cm action Ar name -If at least one mail envelope matches the -.Ar options -of one -.Ic match Cm action -directive, receive the incoming message, put a copy into each -matching envelope, and atomically save the envelopes to the mail -spool for later processing by the respective dispatcher -.Ar name . -.Pp -The following matching options are supported and can all be negated: -.Bl -tag -width Ds -.It Xo -.Op Ic \&! -.Cm for any -.Xc -Specify that session may address any destination. -.It Xo -.Op Ic \&! -.Cm for local -.Xc -Specify that session may address any local domain. -This is the default, and may be omitted. -.It Xo -.Op Ic \&! -.Cm for domain -.Ar domain | Pf < Ar domain Ns > -.Xc -Specify that session may address the string or list table -.Ar domain . -.It Xo -.Op Ic \&! -.Cm for domain regex -.Ar domain | Pf < Ar domain Ns > -.Xc -Specify that session may address the regex or regex table -.Ar domain . -.It Xo -.Op Ic \&! -.Cm for rcpt-to -.Ar recipient | Pf < Ar recipient Ns > -.Xc -Specify that session may address the string or list table -.Ar recipient . -.It Xo -.Op Ic \&! -.Cm for rcpt-to regex -.Ar recipient | Pf < Ar recipient Ns > -.Xc -Specify that session may address the regex or regex table -.Ar recipient . -.It Xo -.Op Ic \&! -.Cm from any -.Xc -Specify that session may originate from any source. -.It Xo -.Op Ic \&! -.Cm from auth -.Xc -Specify that session may originate from any authenticated user, -no matter the source IP address. -.It Xo -.Op Ic \&! -.Cm from auth -.Ar user | Pf < Ar user Ns > -.Xc -Specify that session may originate from authenticated user or user list -.Ar user , -no matter the source IP address. -.It Xo -.Op Ic \&! -.Cm from auth -.Ar user | Pf < Ar user Ns > -.Xc -Specify that session may originate from authenticated regex or regex list -.Ar user , -no matter the source IP address. -.It Xo -.Op Ic \&! -.Cm from local -.Xc -Specify that session may only originate from a local IP address, -or from the local enqueuer. -This is the default, and may be omitted. -.It Xo -.Op Ic \&! -.Cm from mail-from -.Ar sender | Pf < Ar sender Ns > -.Xc -Specify that session may originate from sender or sender list -.Ar sender , -no matter the source IP address. -.It Xo -.Op Ic \&! -.Cm from mail-from regex -.Ar sender | Pf < Ar sender Ns > -.Xc -Specify that session may originate from regex or regex list -.Ar sender , -no matter the source IP address. -.It Xo -.Op Ic \&! -.Cm from rdns -.Xc -Specify that session may only originate from an IP address that -resolves to a reverse DNS. -.It Xo -.Op Ic \&! -.Cm from rdns -.Ar hostname | Pf < Ar hostname Ns > -.Xc -Specify that session may only originate from an IP address that -resolves to a reverse DNS matching string or list string -.Ar hostname . -.It Xo -.Op Ic \&! -.Cm from rdns regex -.Ar hostname | Pf < Ar hostname Ns > -.Xc -Specify that session may only originate from an IP address that -resolves to a reverse DNS matching regex or list regex -.Ar hostname . -.It Xo -.Op Ic \&! -.Cm from socket -.Xc -Specify that session may only originate from the local enqueuer. -.It Xo -.Op Ic \&! -.Cm from src -.Ar address | Pf < Ar address Ns > -.Xc -Specify that session may only originate from string or list table -.Ar address -which can be a specific address or a subnet expressed in CIDR-notation. -.It Xo -.Op Ic \&! -.Cm from src regex -.Ar address | Pf < Ar address Ns > -.Xc -Specify that session may only originate from regex or regex table -.Ar address -which can be a specific address or a subnet expressed in CIDR-notation. -.El -.Pp -In addition, the following transaction options: -.Bl -tag -width Ds -.It Xo -.Op Ic \&! -.Cm auth -.Xc -Matches transactions which have been authenticated. -.It Xo -.Op Ic \&! -.Cm auth -.Ar username | Pf < Ar username Ns > -.Xc -Matches transactions which have been authenticated for user or user list -.Ar username . -.It Xo -.Op Ic \&! -.Cm auth regex -.Ar username | Pf < Ar username Ns > -.Xc -Matches transactions which have been authenticated for regex or regex list -.Ar username . -.It Xo -.Op Ic \&! -.Cm helo -.Ar helo-name | Pf < Ar helo-name Ns > -.Xc -Specify that session's HELO / EHLO should match the string or list table -.Ar helo-name . -.It Xo -.Op Ic \&! -.Cm helo regex -.Ar helo-name | Pf < Ar helo-name Ns > -.Xc -Specify that session's HELO / EHLO should match the regex or regex table -.Ar helo-name . -.It Xo -.Op Ic \&! -.Cm mail-from -.Ar sender | Pf < Ar sender Ns > -.Xc -Specify that transactions's MAIL FROM should match the string or list table -.Ar sender . -.It Xo -.Op Ic \&! -.Cm mail-from regex -.Ar sender | Pf < Ar sender Ns > -.Xc -Specify that transactions's MAIL FROM should match the regex or regex table -.Ar sender . -.It Xo -.Op Ic \&! -.Cm rcpt-to -.Ar recipient | Pf < Ar recipient Ns > -.Xc -Specify that transaction's RCPT TO should match the string or list table -.Ar recipient . -.It Xo -.Op Ic \&! -.Cm rcpt-to regex -.Ar recipient | Pf < Ar recipient Ns > -.Xc -Specify that transaction's RCPT TO should match the regex or regex table -.Ar recipient . -.It Xo -.Op Ic \&! -.Cm tag Ar tag -.Xc -Matches transactions tagged with the given -.Ar tag . -.It Xo -.Op Ic \&! -.Cm tag regex Ar tag -.Xc -Matches transactions tagged with the given -.Ar tag -regex. -.It Xo -.Op Ic \&! -.Cm tls -.Xc -Specify that transaction should take place in a TLS channel. -.El -.It Ic match Ar options Cm reject -Reject the incoming message during the SMTP dialogue. -The same -.Ar options -are supported as for the -.Ic match Cm action -directive. -.It Ic mda Cm wrapper Ar name command -Associate -.Ar command -with the mail delivery agent wrapper named -.Ar name . -When a local delivery specifies a wrapper, the -.Ar command -associated with the wrapper will be executed instead. -The command may contain format specifiers -.Pq see Sx FORMAT SPECIFIERS . -.It Ic mta Cm max-deferred Ar number -When delivery to a given host is suspended due to temporary failures, -cache at most -.Ar number -envelopes for that host such that they can be delivered -as soon as another delivery succeeds to that host. -The default is 100. -.It Ic pki Ar pkiname Cm cert Ar certfile -Associate certificate file -.Ar certfile -with host -.Ar pkiname , -and use that file to prove the identity of the mail server to clients. -.Ar pkiname -is the server's name, -derived from the default hostname -or set using either -.Pa /etc/mail/mailname -or using the -.Ic hostname -directive. -If a fallback certificate or SNI is wanted, the -.Sq * -wildcard may be used as -.Ar pkiname . -.Pp -A certificate chain may be created by appending one or many certificates, -including a Certificate Authority certificate, -to -.Ar certfile . -The creation of certificates is documented in -.Xr starttls 8 . -.It Ic pki Ar pkiname Cm key Ar keyfile -Associate the key located in -.Ar keyfile -with host -.Ar pkiname . -.It Ic pki Ar pkiname Cm dhe Ar params -Specify the DHE parameters to use for DHE cipher suites with host -.Ar pkiname . -Valid parameter values are -.Cm none , -.Cm legacy , -and -.Cm auto . -For -.Cm legacy , -a fixed key length of 1024 bits is used, whereas for -.Cm auto , -the key length is determined automatically. -The default is -.Cm none , -which disables DHE cipher suites. -.It Ic proc Ar proc-name Ar command -Register an external process named -.Ar proc-name -from -.Ar command . -Such processes may be used to share the same instance between multiple filters. -If -.Ar command -starts with a slash it is executed with an absolute path, -else it will be run from -.Dq /usr/local/libexec/smtpd/ . -.It Ic queue Cm compression -Store queue files in a compressed format. -This may be useful to save disk space. -.It Ic queue Cm encryption Op Ar key -Encrypt queue files with -.Xr EVP_aes_256_gcm 3 . -If no -.Ar key -is specified, it is read with -.Xr getpass 3 . -If the string -.Cm stdin -or a single dash -.Pq Ql - -is given instead of a -.Ar key , -the key is read from the standard input. -.It Ic queue Cm ttl Ar delay -Set the default expiration time for temporarily undeliverable -messages, given as a positive decimal integer followed by a unit -.Cm s , m , h , -or -.Cm d . -The default is four days -.Pq 4d . -.It Ic smtp Cm ciphers Ar control -Set the -.Ar control -string for -.Xr SSL_CTX_set_cipher_list 3 . -The default is -.Qq HIGH:!aNULL:!MD5 . -.It Ic smtp limit Cm max-mails Ar count -Limit the number of messages to -.Ar count -for each session. -The default is 100. -.It Ic smtp limit Cm max-rcpt Ar count -Limit the number of recipients to -.Ar count -for each transaction. -The default is 1000. -.It Ic smtp Cm max-message-size Ar size -Reject messages larger than -.Ar size , -given as a positive number of bytes or as a string to be parsed with -.Xr scan_scaled 3 . -The default is -.Qq 35M . -.It Ic smtp Cm sub-addr-delim Ar character -When resolving the local part of a local email address, ignore the ASCII -.Ar character -and all characters following it. -The default is -.Ql + . -.It Ic srs Cm key Ar secret -Set the secret key to use for SRS, -the Sender Rewriting Scheme. -.It Ic srs Cm key backup Ar secret -Set a backup secret key to use as a fallback for SRS. -This can be used to implement SRS key rotation. -.It Ic srs Cm ttl Ar delay -Set the time-to-live delay for SRS envelopes. -After this delay, -a bounce reply to the SRS address will be discarded to limit risks of forged addresses. -The default is four days -.Pq 4d . -.It Ic table Ar name Oo Ar type : Oc Ns Ar pathname -Tables provide additional configuration information for -.Xr smtpd 8 -in the form of lists or key-value mappings. -The format of the entries depends on what the table is used for. -Refer to -.Xr table 5 -for the exhaustive documentation. -.Pp -Each table is identified by an arbitrary, unique -.Ar name . -.Pp -If the -.Ar type -is -.Cm db , -information is stored in a file created with -.Xr makemap 8 ; -if it is -.Cm file -or omitted, information is stored in a plain text file -using the format described in -.Xr table 5 . -The -.Ar pathname -to the file must be absolute. -.It Ic table Ar name Brq Ar value Op , Ar ... -Instead of using a separate file, declare a list table -containing the given static -.Ar value Ns s . -The table must contain at least one value and may declare multiple values as a -comma-separated (whitespace optional) list. -.It Ic table Ar name Brq Ar key Ns = Ns Ar value Op , Ar ... -Instead of using a separate file, declare a mapping table -containing the given static -.Ar key Ns - Ns Ar value -pairs. -The table must contain at least one key-value pair and may declare -multiple pairs as a comma-separated (whitespace optional) list. -.El -.Ss MAIL FILTERING -In a regular workflow, -.Xr smtpd 8 -may accept or reject a message based only on the content of envelopes. -Its decisions are about the handling of the message, -not about the handling of an active session. -.Pp -Filtering extends the decision making process by allowing -.Xr smtpd 8 -to stop at each phase of an SMTP session, -check that conditions are met, -then decide if a session is allowed to move forward. -.Pp -With filtering, -a session may be interrupted at any phase before an envelope is complete. -A message may also be rejected after being submitted, -regardless of whether the envelope was accepted or not. -.Pp -The following phases are currently supported: -.Bl -column mail-from -offset indent -.It connect Ta upon connection, before a banner is displayed -.It helo Ta after HELO command is submitted -.It ehlo Ta after EHLO command is submitted -.It mail-from Ta after MAIL FROM command is submitted -.It rcpt-to Ta after RCPT TO command is submitted -.It data Ta after DATA command is submitted -.It commit Ta after message is fully is submitted -.El -.Pp -At each phase, various conditions may be matched. -The fcrdns, rdns, and src data are available in all phases, -but other data must have been already submitted before they are available. -.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent -.It fcrdns Ta forward-confirmed reverse DNS is valid -.It rdns Ta session has a reverse DNS -.It rdns Pf < Ar table Ns > Ta session has a reverse DNS in table -.It src Pf < Ar table Ns > Ta source address is in table -.It helo Pf < Ar table Ns > Ta helo name is in table -.It auth Ta session is authenticated -.It auth Pf < Ar table Ns > Ta session username is in table -.It mail-from Pf < Ar table Ns > Ta sender address is in table -.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table -.El -.Pp -These conditions may all be negated by prefixing them with an exclamation mark: -.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent -.It !fcrdns Ta forward-confirmed reverse DNS is invalid -.El -.Pp -Any conditions using a table may indicate that tables hold regex by -prefixing the table name with the keyword regex. -.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent -.It helo regex Pf < Ar table Ns > Ta helo name matches a regex in table -.El -.Pp -Finally, a number of decisions may be taken: -.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent -.It bypass Ta the session or transaction bypasses filters -.It disconnect Ar message Ta the session is disconnected with message -.It junk Ta the session or transaction is junked, i.e., an -.Ql X-Spam: yes -header is added to any messages -.It reject Ar message Ta the command is rejected with message -.It rewrite Ar value Ta the command parameter is rewritten with value -.El -.Pp -Decisions that involve a message require that the message be RFC valid, -meaning that they should either start with a 4xx or 5xx status code. -Descisions can be taken at any phase, -though junking can only happen before a message is committed. -.Ss FORMAT SPECIFIERS -Some configuration directives support expansion of their parameters at runtime. -Such directives (for example -.Ic action Cm maildir , -.Ic action Cm mda ) -may use format specifiers which are expanded before delivery or -relaying. -The following formats are currently supported: -.Bl -column %{user.directory} -offset indent -.It %{sender} Ta sender email address, may be empty string -.It %{sender.user} Ta user part of the sender email address, may be empty -.It %{sender.domain} Ta domain part of the sender email address, may be empty -.It %{rcpt} Ta recipient email address -.It %{rcpt.user} Ta user part of the recipient email address -.It %{rcpt.domain} Ta domain part of the recipient email address -.It %{dest} Ta recipient email address after expansion -.It %{dest.user} Ta user part after expansion -.It %{dest.domain} Ta domain part after expansion -.It %{user.username} Ta local user -.It %{user.directory} Ta home directory of the local user -.It %{mbox.from} Ta name used in mbox From separator lines -.It %{mda} Ta mda command, only available for mda wrappers -.El -.Pp -Expansion formats also support partial expansion using the optional -bracket notations with substring offset. -For example, with recipient domain -.Dq example.org : -.Bl -column %{rcpt.domain[0:-4]} -offset indent -.It %{rcpt.domain[0]} Ta expands to Dq e -.It %{rcpt.domain[1]} Ta expands to Dq x -.It %{rcpt.domain[8:]} Ta expands to Dq org -.It %{rcpt.domain[-3:]} Ta expands to Dq org -.It %{rcpt.domain[0:6]} Ta expands to Dq example -.It %{rcpt.domain[0:-4]} Ta expands to Dq example -.El -.Pp -In addition, modifiers may be applied to the token. -For example, with recipient -.Dq User+Tag@Example.org : -.Bl -column %{rcpt:lowercase|strip} -offset indent -.It %{rcpt:lowercase} Ta expands to Dq user+tag@example.org -.It %{rcpt:uppercase} Ta expands to Dq USER+TAG@EXAMPLE.ORG -.It %{rcpt:strip} Ta expands to Dq User@Example.org -.It %{rcpt:lowercase|strip} Ta expands to Dq user@example.org -.El -.Pp -For security concerns, expanded values are sanitized and potentially -dangerous characters are replaced with -.Sq \&: . -In situations where they are desirable, the -.Dq raw -modifier may be applied. -For example, with recipient -.Dq user+t?g@example.org : -.Bl -column %{rcpt:raw} -offset indent -.It %{rcpt} Ta expands to Dq user+t:g@example.org -.It %{rcpt:raw} Ta expands to Dq user+t?g@example.org -.El -.Sh FILES -.Bl -tag -width "/etc/mail/smtpd.confXXX" -compact -.It Pa /etc/mail/smtpd.conf -Default -.Xr smtpd 8 -configuration file. -.It Pa /etc/mail/mailname -If this file exists, -the first line is used as the server name. -Otherwise, the server name is derived from the local hostname returned by -.Xr gethostname 3 , -either directly if it is a fully qualified domain name, -or by retrieving the associated canonical name through -.Xr getaddrinfo 3 . -.It Pa /var/run/smtpd.sock -Unix domain socket for incoming SMTP connections. -.It Pa /var/spool/smtpd/ -Spool directories for mail during processing. -.El -.Sh EXAMPLES -The default -.Nm -file which ships with -.Ox -listens on the loopback network interface -.Pq Pa lo0 -and allows for mail from users and daemons on the local machine, -as well as permitting email to remote servers. -Some more complex configurations are given below. -.Pp -This first example is the same as the default configuration, -but all outgoing mail is forwarded to a remote SMTP server. -A secrets file is needed to specify a username and password: -.Bd -literal -offset indent -# touch /etc/mail/secrets -# chmod 640 /etc/mail/secrets -# chown root:_smtpd /etc/mail/secrets -# echo "bob username:password" > /etc/mail/secrets -.Ed -.Pp -.Nm -would look like this: -.Bd -literal -offset indent -table aliases file:/etc/mail/aliases -table secrets file:/etc/mail/secrets - -listen on lo0 - -action "local_mail" mbox alias <aliases> -action "outbound" relay host smtp+tls://bob@smtp.example.com \e - auth <secrets> - -match from local for local action "local_mail" -match from local for any action "outbound" -.Ed -.Pp -In this second example, -the aim is to permit mail delivery and relaying only for users that can authenticate -(using their normal login credentials). -An RSA certificate must be provided to prove the server's identity. -The mail server listens on all interfaces the default routes point to. -Mail with a local destination is sent to an external MDA. -First, the RSA certificate is created: -.Bd -literal -offset indent -# openssl genrsa \-out /etc/ssl/private/mail.example.com.key 4096 -# openssl req \-new \-x509 \-key /etc/ssl/private/mail.example.com.key \e - \-out /etc/ssl/mail.example.com.crt \-days 365 -# chmod 600 /etc/ssl/mail.example.com.crt -# chmod 600 /etc/ssl/private/mail.example.com.key -.Ed -.Pp -In the example above, -a certificate valid for one year was created. -The configuration file would look like this: -.Bd -literal -offset indent -pki mail.example.com cert "/etc/ssl/mail.example.com.crt" -pki mail.example.com key "/etc/ssl/private/mail.example.com.key" - -table aliases file:/etc/mail/aliases - -listen on lo0 -listen on egress tls pki mail.example.com auth - -action mda_with_aliases mda "/path/to/mda \-f \-" alias <aliases> -action mda_without_aliases mda "/path/to/mda \-f \-" -action "outbound" relay - -match for local action mda_with_aliases -match from any for domain example.com action mda_without_aliases -match for any action "outbound" -match auth from any for any action "outbound" -.Ed -.Pp -For sites that wish to sign messages using DKIM, -the following example uses -.Sy opensmtpd-filter-dkimsign -for DKIM signing: -.Bd -literal -offset indent -table aliases file:/etc/mail/aliases - -filter "dkimsign" proc-exec "filter-dkimsign -d <domain> -s <selector> \e - -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign - -listen on socket filter "dkimsign" -listen on lo0 filter "dkimsign" - -action "local_mail" mbox alias <aliases> -action "outbound" relay - -match for local action "local_mail" -match for any action "outbound" -.Ed -.Pp -Alternatively, the -.Sy opensmtpd-filter-rspamd -package may be used to provide integration with -.Sy rspamd , -a third-party daemon which provides multiple antispam features -as well as DKIM signing. -As well as configuring -.Sy rspamd -itself, -it requires use of the -.Cm proc-exec -keyword: -.Bd -literal -offset indent -filter "rspamd" proc-exec "filter-rspamd" -.Ed -.Pp -Sites that accept non-local messages may be able to cut down on the -volume of spam received by rejecting forged messages that claim -to be from the local domain. -The following example uses a list table -.Em other-relays -to specify the IP addresses of relays that may legitimately -originate mail with the owner's domain as the sender. -.Bd -literal -offset indent -table aliases file:/etc/mail/aliases -table other-relays file:/etc/mail/other-relays - -listen on lo0 -listen on egress - -action "local_mail" mbox alias <aliases> -action "outbound" relay - -match for local action "local_mail" -match for any action "outbound" -match !from src <other-relays> mail\-from "@example.com" for any \e - reject -match from any for domain example.com action "local_mail" -.Ed -.Sh SEE ALSO -.Xr mailer.conf 5 , -.Xr table 5 , -.Xr makemap 8 , -.Xr smtpd 8 -.Sh HISTORY -.Xr smtpd 8 -first appeared in -.Ox 4.6 . diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h deleted file mode 100644 index 4385f747..00000000 --- a/smtpd/smtpd.h +++ /dev/null @@ -1,1784 +0,0 @@ -/* $OpenBSD: smtpd.h,v 1.656 2020/04/08 07:30:44 eric Exp $ */ - -/* - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <event.h> - -#include <imsg.h> - -#include "openbsd-compat.h" - -#ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) -#endif - -#include <netinet/in.h> -#include <netdb.h> -#include <event.h> - -#include "smtpd-defines.h" -#include "smtpd-api.h" -#include "ioev.h" - -#define CHECK_IMSG_DATA_SIZE(imsg, expected_sz) do { \ - if ((imsg)->hdr.len - IMSG_HEADER_SIZE != (expected_sz)) \ - fatalx("smtpd: imsg %d: data size expected %zd got %zd",\ - (imsg)->hdr.type, \ - (expected_sz), (imsg)->hdr.len - IMSG_HEADER_SIZE); \ -} while (0) - -#ifndef SMTPD_CONFDIR -#define SMTPD_CONFDIR "/etc" -#endif -#define CONF_FILE SMTPD_CONFDIR "/smtpd.conf" -#define MAILNAME_FILE SMTPD_CONFDIR "/mailname" -#ifndef CA_FILE -#define CA_FILE "/etc/ssl/cert.pem" -#endif - -#define PROC_COUNT 7 - -#define MAX_HOPS_COUNT 100 -#define DEFAULT_MAX_BODY_SIZE (35*1024*1024) - -#define EXPAND_BUFFER 1024 - -#define SMTPD_QUEUE_EXPIRY (4 * 24 * 60 * 60) -#ifndef SMTPD_USER -#define SMTPD_USER "_smtpd" -#endif -#ifndef SMTPD_QUEUE_USER -#define SMTPD_QUEUE_USER "_smtpq" -#endif -#ifndef SMTPD_SOCKDIR -#define SMTPD_SOCKDIR "/var/run" -#endif -#define SMTPD_SOCKET SMTPD_SOCKDIR "/smtpd.sock" -#ifndef SMTPD_NAME -#define SMTPD_NAME "OpenSMTPD" -#endif -#define SMTPD_VERSION "6.7.0-portable" -#define SMTPD_SESSION_TIMEOUT 300 -#define SMTPD_BACKLOG 5 - -#ifndef PATH_SMTPCTL -#define PATH_SMTPCTL "/usr/sbin/smtpctl" -#endif - -#define PATH_OFFLINE "/offline" -#define PATH_PURGE "/purge" -#define PATH_TEMPORARY "/temporary" - -#ifndef PATH_LIBEXEC -#define PATH_LIBEXEC "/usr/local/libexec/smtpd" -#endif - - -/* - * RFC 5322 defines these characters as valid, some of them are - * potentially dangerous and need to be escaped. - */ -#define MAILADDR_ALLOWED "!#$%&'*/?^`{|}~+-=_" -#define MAILADDR_ESCAPE "!#$%&'*?`{|}~" - - -#define F_STARTTLS 0x01 -#define F_SMTPS 0x02 -#define F_SSL (F_STARTTLS | F_SMTPS) -#define F_AUTH 0x08 -#define F_STARTTLS_REQUIRE 0x20 -#define F_AUTH_REQUIRE 0x40 -#define F_MASK_SOURCE 0x100 -#define F_TLS_VERIFY 0x200 -#define F_EXT_DSN 0x400 -#define F_RECEIVEDAUTH 0x800 -#define F_MASQUERADE 0x1000 -#define F_FILTERED 0x2000 -#define F_PROXY 0x4000 - -#define RELAY_TLS_OPPORTUNISTIC 0 -#define RELAY_TLS_STARTTLS 1 -#define RELAY_TLS_SMTPS 2 -#define RELAY_TLS_NO 3 - -#define RELAY_AUTH 0x08 -#define RELAY_LMTP 0x80 -#define RELAY_TLS_VERIFY 0x200 - -#define MTA_EXT_DSN 0x400 - - -#define P_SENDMAIL 0 -#define P_NEWALIASES 1 -#define P_MAKEMAP 2 - -#define CERT_ERROR -1 -#define CERT_OK 0 -#define CERT_NOCA 1 -#define CERT_NOCERT 2 -#define CERT_INVALID 3 - -struct userinfo { - char username[SMTPD_VUSERNAME_SIZE]; - char directory[PATH_MAX]; - uid_t uid; - gid_t gid; -}; - -struct netaddr { - struct sockaddr_storage ss; - int bits; -}; - -struct relayhost { - uint16_t flags; - int tls; - char hostname[HOST_NAME_MAX+1]; - uint16_t port; - char authlabel[PATH_MAX]; -}; - -struct credentials { - char username[LINE_MAX]; - char password[LINE_MAX]; -}; - -struct destination { - char name[HOST_NAME_MAX+1]; -}; - -struct source { - struct sockaddr_storage addr; -}; - -struct addrname { - struct sockaddr_storage addr; - char name[HOST_NAME_MAX+1]; -}; - -union lookup { - struct expand *expand; - struct credentials creds; - struct netaddr netaddr; - struct source source; - struct destination domain; - struct userinfo userinfo; - struct mailaddr mailaddr; - struct addrname addrname; - struct maddrmap *maddrmap; - char relayhost[LINE_MAX]; -}; - -/* - * Bump IMSG_VERSION whenever a change is made to enum imsg_type. - * This will ensure that we can never use a wrong version of smtpctl with smtpd. - */ -#define IMSG_VERSION 16 - -enum imsg_type { - IMSG_NONE, - - IMSG_CTL_OK, - IMSG_CTL_FAIL, - - IMSG_CTL_GET_DIGEST, - IMSG_CTL_GET_STATS, - IMSG_CTL_LIST_MESSAGES, - IMSG_CTL_LIST_ENVELOPES, - IMSG_CTL_MTA_SHOW_HOSTS, - IMSG_CTL_MTA_SHOW_RELAYS, - IMSG_CTL_MTA_SHOW_ROUTES, - IMSG_CTL_MTA_SHOW_HOSTSTATS, - IMSG_CTL_MTA_BLOCK, - IMSG_CTL_MTA_UNBLOCK, - IMSG_CTL_MTA_SHOW_BLOCK, - IMSG_CTL_PAUSE_EVP, - IMSG_CTL_PAUSE_MDA, - IMSG_CTL_PAUSE_MTA, - IMSG_CTL_PAUSE_SMTP, - IMSG_CTL_PROFILE, - IMSG_CTL_PROFILE_DISABLE, - IMSG_CTL_PROFILE_ENABLE, - IMSG_CTL_RESUME_EVP, - IMSG_CTL_RESUME_MDA, - IMSG_CTL_RESUME_MTA, - IMSG_CTL_RESUME_SMTP, - IMSG_CTL_RESUME_ROUTE, - IMSG_CTL_REMOVE, - IMSG_CTL_SCHEDULE, - IMSG_CTL_SHOW_STATUS, - IMSG_CTL_TRACE_DISABLE, - IMSG_CTL_TRACE_ENABLE, - IMSG_CTL_UPDATE_TABLE, - IMSG_CTL_VERBOSE, - IMSG_CTL_DISCOVER_EVPID, - IMSG_CTL_DISCOVER_MSGID, - - IMSG_CTL_SMTP_SESSION, - - IMSG_GETADDRINFO, - IMSG_GETADDRINFO_END, - IMSG_GETNAMEINFO, - IMSG_RES_QUERY, - - IMSG_CERT_INIT, - IMSG_CERT_CERTIFICATE, - IMSG_CERT_VERIFY, - - IMSG_SETUP_KEY, - IMSG_SETUP_PEER, - IMSG_SETUP_DONE, - - IMSG_CONF_START, - IMSG_CONF_END, - - IMSG_STAT_INCREMENT, - IMSG_STAT_DECREMENT, - IMSG_STAT_SET, - - IMSG_LKA_AUTHENTICATE, - IMSG_LKA_OPEN_FORWARD, - IMSG_LKA_ENVELOPE_SUBMIT, - IMSG_LKA_ENVELOPE_COMMIT, - - IMSG_QUEUE_DELIVER, - IMSG_QUEUE_DELIVERY_OK, - IMSG_QUEUE_DELIVERY_TEMPFAIL, - IMSG_QUEUE_DELIVERY_PERMFAIL, - IMSG_QUEUE_DELIVERY_LOOP, - IMSG_QUEUE_DISCOVER_EVPID, - IMSG_QUEUE_DISCOVER_MSGID, - IMSG_QUEUE_ENVELOPE_ACK, - IMSG_QUEUE_ENVELOPE_COMMIT, - IMSG_QUEUE_ENVELOPE_REMOVE, - IMSG_QUEUE_ENVELOPE_SCHEDULE, - IMSG_QUEUE_ENVELOPE_SUBMIT, - IMSG_QUEUE_HOLDQ_HOLD, - IMSG_QUEUE_HOLDQ_RELEASE, - IMSG_QUEUE_MESSAGE_COMMIT, - IMSG_QUEUE_MESSAGE_ROLLBACK, - IMSG_QUEUE_SMTP_SESSION, - IMSG_QUEUE_TRANSFER, - - IMSG_MDA_DELIVERY_OK, - IMSG_MDA_DELIVERY_TEMPFAIL, - IMSG_MDA_DELIVERY_PERMFAIL, - IMSG_MDA_DELIVERY_LOOP, - IMSG_MDA_DELIVERY_HOLD, - IMSG_MDA_DONE, - IMSG_MDA_FORK, - IMSG_MDA_HOLDQ_RELEASE, - IMSG_MDA_LOOKUP_USERINFO, - IMSG_MDA_KILL, - IMSG_MDA_OPEN_MESSAGE, - - IMSG_MTA_DELIVERY_OK, - IMSG_MTA_DELIVERY_TEMPFAIL, - IMSG_MTA_DELIVERY_PERMFAIL, - IMSG_MTA_DELIVERY_LOOP, - IMSG_MTA_DELIVERY_HOLD, - IMSG_MTA_DNS_HOST, - IMSG_MTA_DNS_HOST_END, - IMSG_MTA_DNS_MX, - IMSG_MTA_DNS_MX_PREFERENCE, - IMSG_MTA_HOLDQ_RELEASE, - IMSG_MTA_LOOKUP_CREDENTIALS, - IMSG_MTA_LOOKUP_SOURCE, - IMSG_MTA_LOOKUP_HELO, - IMSG_MTA_LOOKUP_SMARTHOST, - IMSG_MTA_OPEN_MESSAGE, - IMSG_MTA_SCHEDULE, - - IMSG_SCHED_ENVELOPE_BOUNCE, - IMSG_SCHED_ENVELOPE_DELIVER, - IMSG_SCHED_ENVELOPE_EXPIRE, - IMSG_SCHED_ENVELOPE_INJECT, - IMSG_SCHED_ENVELOPE_REMOVE, - IMSG_SCHED_ENVELOPE_TRANSFER, - - IMSG_SMTP_AUTHENTICATE, - IMSG_SMTP_MESSAGE_COMMIT, - IMSG_SMTP_MESSAGE_CREATE, - IMSG_SMTP_MESSAGE_ROLLBACK, - IMSG_SMTP_MESSAGE_OPEN, - IMSG_SMTP_CHECK_SENDER, - IMSG_SMTP_EXPAND_RCPT, - IMSG_SMTP_LOOKUP_HELO, - - IMSG_SMTP_REQ_CONNECT, - IMSG_SMTP_REQ_HELO, - IMSG_SMTP_REQ_MAIL, - IMSG_SMTP_REQ_RCPT, - IMSG_SMTP_REQ_DATA, - IMSG_SMTP_REQ_EOM, - IMSG_SMTP_EVENT_RSET, - IMSG_SMTP_EVENT_COMMIT, - IMSG_SMTP_EVENT_ROLLBACK, - IMSG_SMTP_EVENT_DISCONNECT, - - IMSG_LKA_PROCESSOR_FORK, - IMSG_LKA_PROCESSOR_ERRFD, - - IMSG_REPORT_SMTP_LINK_CONNECT, - IMSG_REPORT_SMTP_LINK_DISCONNECT, - IMSG_REPORT_SMTP_LINK_GREETING, - IMSG_REPORT_SMTP_LINK_IDENTIFY, - IMSG_REPORT_SMTP_LINK_TLS, - IMSG_REPORT_SMTP_LINK_AUTH, - IMSG_REPORT_SMTP_TX_RESET, - IMSG_REPORT_SMTP_TX_BEGIN, - IMSG_REPORT_SMTP_TX_MAIL, - IMSG_REPORT_SMTP_TX_RCPT, - IMSG_REPORT_SMTP_TX_ENVELOPE, - IMSG_REPORT_SMTP_TX_DATA, - IMSG_REPORT_SMTP_TX_COMMIT, - IMSG_REPORT_SMTP_TX_ROLLBACK, - IMSG_REPORT_SMTP_PROTOCOL_CLIENT, - IMSG_REPORT_SMTP_PROTOCOL_SERVER, - IMSG_REPORT_SMTP_FILTER_RESPONSE, - IMSG_REPORT_SMTP_TIMEOUT, - - IMSG_FILTER_SMTP_BEGIN, - IMSG_FILTER_SMTP_END, - IMSG_FILTER_SMTP_PROTOCOL, - IMSG_FILTER_SMTP_DATA_BEGIN, - IMSG_FILTER_SMTP_DATA_END, - - IMSG_CA_RSA_PRIVENC, - IMSG_CA_RSA_PRIVDEC, - IMSG_CA_ECDSA_SIGN, -}; - -enum smtp_proc_type { - PROC_PARENT = 0, - PROC_LKA, - PROC_QUEUE, - PROC_CONTROL, - PROC_SCHEDULER, - PROC_PONY, - PROC_CA, - PROC_PROCESSOR, - PROC_CLIENT, -}; - -enum table_type { - T_NONE = 0, - T_DYNAMIC = 0x01, /* table with external source */ - T_LIST = 0x02, /* table holding a list */ - T_HASH = 0x04, /* table holding a hash table */ -}; - -struct table { - char t_name[LINE_MAX]; - enum table_type t_type; - char t_config[PATH_MAX]; - - void *t_handle; - struct table_backend *t_backend; -}; - -struct table_backend { - const char *name; - const unsigned int services; - int (*config)(struct table *); - int (*add)(struct table *, const char *, const char *); - void (*dump)(struct table *); - int (*open)(struct table *); - int (*update)(struct table *); - void (*close)(struct table *); - int (*lookup)(struct table *, enum table_service, const char *, char **); - int (*fetch)(struct table *, enum table_service, char **); -}; - - -enum bounce_type { - B_FAILED, - B_DELAYED, - B_DELIVERED -}; - -enum dsn_ret { - DSN_RETFULL = 1, - DSN_RETHDRS -}; - -struct delivery_bounce { - enum bounce_type type; - time_t delay; - time_t ttl; - enum dsn_ret dsn_ret; - int mta_without_dsn; -}; - -enum expand_type { - EXPAND_INVALID, - EXPAND_USERNAME, - EXPAND_FILENAME, - EXPAND_FILTER, - EXPAND_INCLUDE, - EXPAND_ADDRESS, - EXPAND_ERROR, -}; - -enum filter_phase { - FILTER_CONNECT, - FILTER_HELO, - FILTER_EHLO, - FILTER_STARTTLS, - FILTER_AUTH, - FILTER_MAIL_FROM, - FILTER_RCPT_TO, - FILTER_DATA, - FILTER_DATA_LINE, - FILTER_RSET, - FILTER_QUIT, - FILTER_NOOP, - FILTER_HELP, - FILTER_WIZ, - FILTER_COMMIT, - FILTER_PHASES_COUNT /* must be last */ -}; - -struct expandnode { - RB_ENTRY(expandnode) entry; - TAILQ_ENTRY(expandnode) tq_entry; - enum expand_type type; - int sameuser; - int realuser; - int forwarded; - struct rule *rule; - struct expandnode *parent; - unsigned int depth; - union { - /* - * user field handles both expansion user and system user - * so we MUST make it large enough to fit a mailaddr user - */ - char user[SMTPD_MAXLOCALPARTSIZE]; - char buffer[EXPAND_BUFFER]; - struct mailaddr mailaddr; - } u; - char subaddress[SMTPD_SUBADDRESS_SIZE]; -}; - -struct expand { - RB_HEAD(expandtree, expandnode) tree; - TAILQ_HEAD(xnodes, expandnode) *queue; - size_t nb_nodes; - struct rule *rule; - struct expandnode *parent; -}; - -struct maddrnode { - TAILQ_ENTRY(maddrnode) entries; - struct mailaddr mailaddr; -}; - -struct maddrmap { - TAILQ_HEAD(xmaddr, maddrnode) queue; -}; - -#define DSN_SUCCESS 0x01 -#define DSN_FAILURE 0x02 -#define DSN_DELAY 0x04 -#define DSN_NEVER 0x08 - -#define DSN_ENVID_LEN 100 - -#define SMTPD_ENVELOPE_VERSION 3 -struct envelope { - TAILQ_ENTRY(envelope) entry; - - char dispatcher[HOST_NAME_MAX+1]; - - char tag[SMTPD_TAG_SIZE]; - - uint32_t version; - uint64_t id; - enum envelope_flags flags; - - char smtpname[HOST_NAME_MAX+1]; - char helo[HOST_NAME_MAX+1]; - char hostname[HOST_NAME_MAX+1]; - char username[SMTPD_MAXMAILADDRSIZE]; - char errorline[LINE_MAX]; - struct sockaddr_storage ss; - - struct mailaddr sender; - struct mailaddr rcpt; - struct mailaddr dest; - - char mda_user[SMTPD_VUSERNAME_SIZE]; - char mda_subaddress[SMTPD_SUBADDRESS_SIZE]; - char mda_exec[LINE_MAX]; - - enum delivery_type type; - union { - struct delivery_bounce bounce; - } agent; - - uint16_t retry; - time_t creation; - time_t ttl; - time_t lasttry; - time_t nexttry; - time_t lastbounce; - - struct mailaddr dsn_orcpt; - char dsn_envid[DSN_ENVID_LEN+1]; - uint8_t dsn_notify; - enum dsn_ret dsn_ret; - - uint8_t esc_class; - uint8_t esc_code; -}; - -struct listener { - uint16_t flags; - int fd; - struct sockaddr_storage ss; - in_port_t port; - struct timeval timeout; - struct event ev; - char filter_name[PATH_MAX]; - char pki_name[PATH_MAX]; - char ca_name[PATH_MAX]; - char tag[SMTPD_TAG_SIZE]; - char authtable[LINE_MAX]; - char hostname[HOST_NAME_MAX+1]; - char hostnametable[PATH_MAX]; - char sendertable[PATH_MAX]; - - TAILQ_ENTRY(listener) entry; - - int local; /* there must be a better way */ -}; - -struct smtpd { - char sc_conffile[PATH_MAX]; - size_t sc_maxsize; - -#define SMTPD_OPT_VERBOSE 0x00000001 -#define SMTPD_OPT_NOACTION 0x00000002 - uint32_t sc_opts; - -#define SMTPD_EXITING 0x00000001 /* unused */ -#define SMTPD_MDA_PAUSED 0x00000002 -#define SMTPD_MTA_PAUSED 0x00000004 -#define SMTPD_SMTP_PAUSED 0x00000008 -#define SMTPD_MDA_BUSY 0x00000010 -#define SMTPD_MTA_BUSY 0x00000020 -#define SMTPD_BOUNCE_BUSY 0x00000040 -#define SMTPD_SMTP_DISABLED 0x00000080 - uint32_t sc_flags; - -#define QUEUE_COMPRESSION 0x00000001 -#define QUEUE_ENCRYPTION 0x00000002 -#define QUEUE_EVPCACHE 0x00000004 - uint32_t sc_queue_flags; - char *sc_queue_key; - size_t sc_queue_evpcache_size; - - size_t sc_session_max_rcpt; - size_t sc_session_max_mails; - - struct dict *sc_mda_wrappers; - size_t sc_mda_max_session; - size_t sc_mda_max_user_session; - size_t sc_mda_task_hiwat; - size_t sc_mda_task_lowat; - size_t sc_mda_task_release; - - size_t sc_mta_max_deferred; - - size_t sc_scheduler_max_inflight; - size_t sc_scheduler_max_evp_batch_size; - size_t sc_scheduler_max_msg_batch_size; - size_t sc_scheduler_max_schedule; - - struct dict *sc_filter_processes_dict; - - int sc_ttl; -#define MAX_BOUNCE_WARN 4 - time_t sc_bounce_warn[MAX_BOUNCE_WARN]; - char sc_hostname[HOST_NAME_MAX+1]; - struct stat_backend *sc_stat; - struct compress_backend *sc_comp; - - time_t sc_uptime; - - /* This is a listener for a local socket used by smtp_enqueue(). */ - struct listener *sc_sock_listener; - - TAILQ_HEAD(listenerlist, listener) *sc_listeners; - - TAILQ_HEAD(rulelist, rule) *sc_rules; - - - struct dict *sc_filters_dict; - struct dict *sc_dispatchers; - struct dispatcher *sc_dispatcher_bounce; - - struct dict *sc_ca_dict; - struct dict *sc_pki_dict; - struct dict *sc_ssl_dict; - - struct dict *sc_tables_dict; /* keyed lookup */ - - struct dict *sc_limits_dict; - - char *sc_tls_ciphers; - - char *sc_subaddressing_delim; - - char *sc_srs_key; - char *sc_srs_key_backup; - int sc_srs_ttl; -}; - -#define TRACE_DEBUG 0x0001 -#define TRACE_IMSG 0x0002 -#define TRACE_IO 0x0004 -#define TRACE_SMTP 0x0008 -#define TRACE_FILTERS 0x0010 -#define TRACE_MTA 0x0020 -#define TRACE_BOUNCE 0x0040 -#define TRACE_SCHEDULER 0x0080 -#define TRACE_LOOKUP 0x0100 -#define TRACE_STAT 0x0200 -#define TRACE_RULES 0x0400 -#define TRACE_MPROC 0x0800 -#define TRACE_EXPAND 0x1000 -#define TRACE_TABLES 0x2000 -#define TRACE_QUEUE 0x4000 - -#define PROFILE_TOSTAT 0x0001 -#define PROFILE_IMSG 0x0002 -#define PROFILE_QUEUE 0x0004 - -struct forward_req { - uint64_t id; - uint8_t status; - - char user[SMTPD_VUSERNAME_SIZE]; - uid_t uid; - gid_t gid; - char directory[PATH_MAX]; -}; - -struct deliver { - char dispatcher[EXPAND_BUFFER]; - - struct mailaddr sender; - struct mailaddr rcpt; - struct mailaddr dest; - - char mda_subaddress[SMTPD_SUBADDRESS_SIZE]; - char mda_exec[LINE_MAX]; - - struct userinfo userinfo; -}; - -struct mta_host { - SPLAY_ENTRY(mta_host) entry; - struct sockaddr *sa; - char *ptrname; - int refcount; - size_t nconn; - time_t lastconn; - time_t lastptrquery; - -#define HOST_IGNORE 0x01 - int flags; -}; - -struct mta_mx { - TAILQ_ENTRY(mta_mx) entry; - struct mta_host *host; - char *mxname; - int preference; -}; - -struct mta_domain { - SPLAY_ENTRY(mta_domain) entry; - char *name; - int as_host; - TAILQ_HEAD(, mta_mx) mxs; - int mxstatus; - int refcount; - size_t nconn; - time_t lastconn; - time_t lastmxquery; -}; - -struct mta_source { - SPLAY_ENTRY(mta_source) entry; - struct sockaddr *sa; - int refcount; - size_t nconn; - time_t lastconn; -}; - -struct mta_connector { - struct mta_source *source; - struct mta_relay *relay; - -#define CONNECTOR_ERROR_FAMILY 0x0001 -#define CONNECTOR_ERROR_SOURCE 0x0002 -#define CONNECTOR_ERROR_MX 0x0004 -#define CONNECTOR_ERROR_ROUTE_NET 0x0008 -#define CONNECTOR_ERROR_ROUTE_SMTP 0x0010 -#define CONNECTOR_ERROR_ROUTE 0x0018 -#define CONNECTOR_ERROR_BLOCKED 0x0020 -#define CONNECTOR_ERROR 0x00ff - -#define CONNECTOR_LIMIT_HOST 0x0100 -#define CONNECTOR_LIMIT_ROUTE 0x0200 -#define CONNECTOR_LIMIT_SOURCE 0x0400 -#define CONNECTOR_LIMIT_RELAY 0x0800 -#define CONNECTOR_LIMIT_CONN 0x1000 -#define CONNECTOR_LIMIT_DOMAIN 0x2000 -#define CONNECTOR_LIMIT 0xff00 - -#define CONNECTOR_NEW 0x10000 -#define CONNECTOR_WAIT 0x20000 - int flags; - - int refcount; - size_t nconn; - time_t lastconn; -}; - -struct mta_route { - SPLAY_ENTRY(mta_route) entry; - uint64_t id; - struct mta_source *src; - struct mta_host *dst; -#define ROUTE_NEW 0x01 -#define ROUTE_RUNQ 0x02 -#define ROUTE_KEEPALIVE 0x04 -#define ROUTE_DISABLED 0xf0 -#define ROUTE_DISABLED_NET 0x10 -#define ROUTE_DISABLED_SMTP 0x20 - int flags; - int nerror; - int penalty; - int refcount; - size_t nconn; - time_t lastconn; - time_t lastdisc; - time_t lastpenalty; -}; - -struct mta_limits { - size_t maxconn_per_host; - size_t maxconn_per_route; - size_t maxconn_per_source; - size_t maxconn_per_connector; - size_t maxconn_per_relay; - size_t maxconn_per_domain; - - time_t conndelay_host; - time_t conndelay_route; - time_t conndelay_source; - time_t conndelay_connector; - time_t conndelay_relay; - time_t conndelay_domain; - - time_t discdelay_route; - - size_t max_mail_per_session; - time_t sessdelay_transaction; - time_t sessdelay_keepalive; - - size_t max_failures_per_session; - - int family; - - int task_hiwat; - int task_lowat; - int task_release; -}; - -struct mta_relay { - SPLAY_ENTRY(mta_relay) entry; - uint64_t id; - - struct dispatcher *dispatcher; - struct mta_domain *domain; - struct mta_limits *limits; - int tls; - int flags; - char *backupname; - int backuppref; - char *sourcetable; - uint16_t port; - char *pki_name; - char *ca_name; - char *authtable; - char *authlabel; - char *helotable; - char *heloname; - char *secret; - int srs; - - int state; - size_t ntask; - TAILQ_HEAD(, mta_task) tasks; - - struct tree connectors; - size_t sourceloop; - time_t lastsource; - time_t nextsource; - - int fail; - char *failstr; - -#define RELAY_WAIT_MX 0x01 -#define RELAY_WAIT_PREFERENCE 0x02 -#define RELAY_WAIT_SECRET 0x04 -#define RELAY_WAIT_LIMITS 0x08 -#define RELAY_WAIT_SOURCE 0x10 -#define RELAY_WAIT_CONNECTOR 0x20 -#define RELAY_WAIT_SMARTHOST 0x40 -#define RELAY_WAITMASK 0x7f - int status; - - int refcount; - size_t nconn; - size_t nconn_ready; - time_t lastconn; -}; - -struct mta_envelope { - TAILQ_ENTRY(mta_envelope) entry; - uint64_t id; - uint64_t session; - time_t creation; - char *smtpname; - char *dest; - char *rcpt; - struct mta_task *task; - int delivery; - - int ext; - char *dsn_orcpt; - char dsn_envid[DSN_ENVID_LEN+1]; - uint8_t dsn_notify; - enum dsn_ret dsn_ret; - - char status[LINE_MAX]; -}; - -struct mta_task { - TAILQ_ENTRY(mta_task) entry; - struct mta_relay *relay; - uint32_t msgid; - TAILQ_HEAD(, mta_envelope) envelopes; - char *sender; -}; - -struct passwd; - -struct queue_backend { - int (*init)(struct passwd *, int, const char *); -}; - -struct compress_backend { - size_t (*compress_chunk)(void *, size_t, void *, size_t); - size_t (*uncompress_chunk)(void *, size_t, void *, size_t); - int (*compress_file)(FILE *, FILE *); - int (*uncompress_file)(FILE *, FILE *); -}; - -/* auth structures */ -enum auth_type { - AUTH_BSD, - AUTH_PWD, -}; - -struct auth_backend { - int (*authenticate)(char *, char *); -}; - -struct scheduler_backend { - int (*init)(const char *); - - int (*insert)(struct scheduler_info *); - size_t (*commit)(uint32_t); - size_t (*rollback)(uint32_t); - - int (*update)(struct scheduler_info *); - int (*delete)(uint64_t); - int (*hold)(uint64_t, uint64_t); - int (*release)(int, uint64_t, int); - - int (*batch)(int, int*, size_t*, uint64_t*, int*); - - size_t (*messages)(uint32_t, uint32_t *, size_t); - size_t (*envelopes)(uint64_t, struct evpstate *, size_t); - int (*schedule)(uint64_t); - int (*remove)(uint64_t); - int (*suspend)(uint64_t); - int (*resume)(uint64_t); - int (*query)(uint64_t); -}; - -enum stat_type { - STAT_COUNTER, - STAT_TIMESTAMP, - STAT_TIMEVAL, - STAT_TIMESPEC, -}; - -struct stat_value { - enum stat_type type; - union stat_v { - size_t counter; - time_t timestamp; - struct timeval tv; - struct timespec ts; - } u; -}; - -#define STAT_KEY_SIZE 1024 -struct stat_kv { - void *iter; - char key[STAT_KEY_SIZE]; - struct stat_value val; -}; - -struct stat_backend { - void (*init)(void); - void (*close)(void); - void (*increment)(const char *, size_t); - void (*decrement)(const char *, size_t); - void (*set)(const char *, const struct stat_value *); - int (*iter)(void **, char **, struct stat_value *); -}; - -struct stat_digest { - time_t startup; - time_t timestamp; - - size_t clt_connect; - size_t clt_disconnect; - - size_t evp_enqueued; - size_t evp_dequeued; - - size_t evp_expired; - size_t evp_removed; - size_t evp_bounce; - - size_t dlv_ok; - size_t dlv_permfail; - size_t dlv_tempfail; - size_t dlv_loop; -}; - - -struct mproc { - pid_t pid; - char *name; - int proc; - void (*handler)(struct mproc *, struct imsg *); - struct imsgbuf imsgbuf; - - char *m_buf; - size_t m_alloc; - size_t m_pos; - uint32_t m_type; - uint32_t m_peerid; - pid_t m_pid; - int m_fd; - - int enable; - short events; - struct event ev; - void *data; -}; - -struct msg { - const uint8_t *pos; - const uint8_t *end; -}; - -extern enum smtp_proc_type smtpd_process; - -extern int tracing; -extern int foreground_log; -extern int profiling; - -extern struct mproc *p_control; -extern struct mproc *p_parent; -extern struct mproc *p_lka; -extern struct mproc *p_queue; -extern struct mproc *p_scheduler; -extern struct mproc *p_pony; -extern struct mproc *p_ca; - -extern struct smtpd *env; -extern void (*imsg_callback)(struct mproc *, struct imsg *); - -/* inter-process structures */ - -struct bounce_req_msg { - uint64_t evpid; - time_t timestamp; - struct delivery_bounce bounce; -}; - -enum dns_error { - DNS_OK = 0, - DNS_RETRY, - DNS_EINVAL, - DNS_ENONAME, - DNS_ENOTFOUND, -}; - -enum lka_resp_status { - LKA_OK, - LKA_TEMPFAIL, - LKA_PERMFAIL -}; - -enum filter_type { - FILTER_TYPE_BUILTIN, - FILTER_TYPE_PROC, - FILTER_TYPE_CHAIN, -}; - -enum filter_subsystem { - FILTER_SUBSYSTEM_SMTP_IN = 1<<0, - FILTER_SUBSYSTEM_SMTP_OUT = 1<<1, -}; - -struct filter_proc { - const char *command; - const char *user; - const char *group; - const char *chroot; - int errfd; - enum filter_subsystem filter_subsystem; -}; - -struct filter_config { - char *name; - enum filter_subsystem filter_subsystem; - enum filter_type filter_type; - enum filter_phase phase; - char *reject; - char *disconnect; - char *rewrite; - char *report; - uint8_t junk; - uint8_t bypass; - char *proc; - - const char **chain; - size_t chain_size; - struct dict chain_procs; - - int8_t not_fcrdns; - int8_t fcrdns; - - int8_t not_rdns; - int8_t rdns; - - int8_t not_rdns_table; - struct table *rdns_table; - - int8_t not_rdns_regex; - struct table *rdns_regex; - - int8_t not_src_table; - struct table *src_table; - - int8_t not_src_regex; - struct table *src_regex; - - int8_t not_helo_table; - struct table *helo_table; - - int8_t not_helo_regex; - struct table *helo_regex; - - int8_t not_auth; - int8_t auth; - - int8_t not_auth_table; - struct table *auth_table; - - int8_t not_auth_regex; - struct table *auth_regex; - - int8_t not_mail_from_table; - struct table *mail_from_table; - - int8_t not_mail_from_regex; - struct table *mail_from_regex; - - int8_t not_rcpt_to_table; - struct table *rcpt_to_table; - - int8_t not_rcpt_to_regex; - struct table *rcpt_to_regex; - -}; - -enum filter_status { - FILTER_PROCEED, - FILTER_REWRITE, - FILTER_REJECT, - FILTER_DISCONNECT, - FILTER_JUNK, -}; - -enum ca_resp_status { - CA_OK, - CA_FAIL -}; - -enum mda_resp_status { - MDA_OK, - MDA_TEMPFAIL, - MDA_PERMFAIL -}; - -struct msg_walkinfo { - struct event ev; - uint32_t msgid; - uint32_t peerid; - size_t n_evp; - void *data; - int done; -}; - - -enum dispatcher_type { - DISPATCHER_LOCAL, - DISPATCHER_REMOTE, - DISPATCHER_BOUNCE, -}; - -struct dispatcher_local { - uint8_t is_mbox; /* only for MBOX */ - - uint8_t expand_only; - uint8_t forward_only; - - char *mda_wrapper; - char *command; - - char *table_alias; - char *table_virtual; - char *table_userbase; - - char *user; -}; - -struct dispatcher_remote { - char *helo; - char *helo_source; - - char *source; - - char *ca; - char *pki; - - char *mail_from; - - char *smarthost; - int smarthost_domain; - - char *auth; - int tls_required; - int tls_noverify; - - int backup; - char *backupmx; - - char *filtername; - - int srs; -}; - -struct dispatcher_bounce { -}; - -struct dispatcher { - enum dispatcher_type type; - union dispatcher_agent { - struct dispatcher_local local; - struct dispatcher_remote remote; - struct dispatcher_bounce bounce; - } u; - - time_t ttl; -}; - -struct rule { - TAILQ_ENTRY(rule) r_entry; - - uint8_t reject; - - int8_t flag_tag; - int8_t flag_from; - int8_t flag_for; - int8_t flag_from_rdns; - int8_t flag_from_socket; - - int8_t flag_tag_regex; - int8_t flag_from_regex; - int8_t flag_for_regex; - - int8_t flag_smtp_helo; - int8_t flag_smtp_starttls; - int8_t flag_smtp_auth; - int8_t flag_smtp_mail_from; - int8_t flag_smtp_rcpt_to; - - int8_t flag_smtp_helo_regex; - int8_t flag_smtp_starttls_regex; - int8_t flag_smtp_auth_regex; - int8_t flag_smtp_mail_from_regex; - int8_t flag_smtp_rcpt_to_regex; - - - char *table_tag; - char *table_from; - char *table_for; - - char *table_smtp_helo; - char *table_smtp_auth; - char *table_smtp_mail_from; - char *table_smtp_rcpt_to; - - char *dispatcher; -}; - - -/* aliases.c */ -int aliases_get(struct expand *, const char *); -int aliases_virtual_get(struct expand *, const struct mailaddr *); -int alias_parse(struct expandnode *, const char *); - - -/* auth.c */ -struct auth_backend *auth_backend_lookup(enum auth_type); - - -/* bounce.c */ -void bounce_add(uint64_t); -void bounce_fd(int); - - -/* ca.c */ -int ca(void); -int ca_X509_verify(void *, void *, const char *, const char *, const char **); -void ca_imsg(struct mproc *, struct imsg *); -void ca_init(void); -void ca_engine_init(void); - - -/* cert.c */ -int cert_init(const char *, int, - void (*)(void *, int, const char *, const void *, size_t), void *); -int cert_verify(const void *, const char *, int, void (*)(void *, int), void *); -void cert_dispatch_request(struct mproc *, struct imsg *); -void cert_dispatch_result(struct mproc *, struct imsg *); - - -/* compress_backend.c */ -struct compress_backend *compress_backend_lookup(const char *); -size_t compress_chunk(void *, size_t, void *, size_t); -size_t uncompress_chunk(void *, size_t, void *, size_t); -int compress_file(FILE *, FILE *); -int uncompress_file(FILE *, FILE *); - -/* config.c */ -#define PURGE_LISTENERS 0x01 -#define PURGE_TABLES 0x02 -#define PURGE_RULES 0x04 -#define PURGE_PKI 0x08 -#define PURGE_PKI_KEYS 0x10 -#define PURGE_DISPATCHERS 0x20 -#define PURGE_EVERYTHING 0xff -struct smtpd *config_default(void); -void purge_config(uint8_t); -void config_process(enum smtp_proc_type); -void config_peer(enum smtp_proc_type); - - -/* control.c */ -int control(void); -int control_create_socket(void); - - -/* crypto.c */ -int crypto_setup(const char *, size_t); -int crypto_encrypt_file(FILE *, FILE *); -int crypto_decrypt_file(FILE *, FILE *); -size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t); -size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t); - - -/* dns.c */ -void dns_imsg(struct mproc *, struct imsg *); - - -/* enqueue.c */ -int enqueue(int, char **, FILE *); - - -/* envelope.c */ -void envelope_set_errormsg(struct envelope *, char *, ...); -void envelope_set_esc_class(struct envelope *, enum enhanced_status_class); -void envelope_set_esc_code(struct envelope *, enum enhanced_status_code); -int envelope_load_buffer(struct envelope *, const char *, size_t); -int envelope_dump_buffer(const struct envelope *, char *, size_t); - - -/* expand.c */ -int expand_cmp(struct expandnode *, struct expandnode *); -void expand_insert(struct expand *, struct expandnode *); -struct expandnode *expand_lookup(struct expand *, struct expandnode *); -void expand_clear(struct expand *); -void expand_free(struct expand *); -int expand_line(struct expand *, const char *, int); -int expand_to_text(struct expand *, char *, size_t); -RB_PROTOTYPE(expandtree, expandnode, nodes, expand_cmp); - - -/* forward.c */ -int forwards_get(int, struct expand *); - - -/* limit.c */ -void limit_mta_set_defaults(struct mta_limits *); -int limit_mta_set(struct mta_limits *, const char*, int64_t); - - -/* lka.c */ -int lka(void); - - -/* lka_proc.c */ -int lka_proc_ready(void); -void lka_proc_forked(const char *, uint32_t, int); -void lka_proc_errfd(const char *, int); -struct io *lka_proc_get_io(const char *); - - -/* lka_report.c */ -void lka_report_init(void); -void lka_report_register_hook(const char *, const char *); -void lka_report_smtp_link_connect(const char *, struct timeval *, uint64_t, const char *, int, - const struct sockaddr_storage *, const struct sockaddr_storage *); -void lka_report_smtp_link_disconnect(const char *, struct timeval *, uint64_t); -void lka_report_smtp_link_greeting(const char *, uint64_t, struct timeval *, - const char *); -void lka_report_smtp_link_identify(const char *, struct timeval *, uint64_t, const char *, const char *); -void lka_report_smtp_link_tls(const char *, struct timeval *, uint64_t, const char *); -void lka_report_smtp_link_auth(const char *, struct timeval *, uint64_t, const char *, const char *); -void lka_report_smtp_tx_reset(const char *, struct timeval *, uint64_t, uint32_t); -void lka_report_smtp_tx_begin(const char *, struct timeval *, uint64_t, uint32_t); -void lka_report_smtp_tx_mail(const char *, struct timeval *, uint64_t, uint32_t, const char *, int); -void lka_report_smtp_tx_rcpt(const char *, struct timeval *, uint64_t, uint32_t, const char *, int); -void lka_report_smtp_tx_envelope(const char *, struct timeval *, uint64_t, uint32_t, uint64_t); -void lka_report_smtp_tx_commit(const char *, struct timeval *, uint64_t, uint32_t, size_t); -void lka_report_smtp_tx_data(const char *, struct timeval *, uint64_t, uint32_t, int); -void lka_report_smtp_tx_rollback(const char *, struct timeval *, uint64_t, uint32_t); -void lka_report_smtp_protocol_client(const char *, struct timeval *, uint64_t, const char *); -void lka_report_smtp_protocol_server(const char *, struct timeval *, uint64_t, const char *); -void lka_report_smtp_filter_response(const char *, struct timeval *, uint64_t, - int, int, const char *); -void lka_report_smtp_timeout(const char *, struct timeval *, uint64_t); -void lka_report_filter_report(uint64_t, const char *, int, const char *, - struct timeval *, const char *); -void lka_report_proc(const char *, const char *); - - -/* lka_filter.c */ -void lka_filter_init(void); -void lka_filter_register_hook(const char *, const char *); -void lka_filter_ready(void); -int lka_filter_proc_in_session(uint64_t, const char *); -void lka_filter_begin(uint64_t, const char *); -void lka_filter_end(uint64_t); -void lka_filter_protocol(uint64_t, enum filter_phase, const char *); -void lka_filter_data_begin(uint64_t); -void lka_filter_data_end(uint64_t); -int lka_filter_response(uint64_t, const char *, const char *); - - -/* lka_session.c */ -void lka_session(uint64_t, struct envelope *); -void lka_session_forward_reply(struct forward_req *, int); - - -/* log.c */ -void vlog(int, const char *, va_list); -void logit(int, const char *, ...) __attribute__((format (printf, 2, 3))); - - -/* mda.c */ -void mda_postfork(void); -void mda_postprivdrop(void); -void mda_imsg(struct mproc *, struct imsg *); - - -/* mda_mbox.c */ -void mda_mbox_init(struct deliver *); -void mda_mbox(struct deliver *); - - -/* mda_unpriv.c */ -void mda_unpriv(struct dispatcher *, struct deliver *, const char *, const char *); - - -/* mda_variables.c */ -ssize_t mda_expand_format(char *, size_t, const struct deliver *, - const struct userinfo *, const char *); - - -/* makemap.c */ -int makemap(int, int, char **); - - -/* mailaddr.c */ -int mailaddr_line(struct maddrmap *, const char *); -void maddrmap_init(struct maddrmap *); -void maddrmap_insert(struct maddrmap *, struct maddrnode *); -void maddrmap_free(struct maddrmap *); - - -/* mproc.c */ -int mproc_fork(struct mproc *, const char*, char **); -void mproc_init(struct mproc *, int); -void mproc_clear(struct mproc *); -void mproc_enable(struct mproc *); -void mproc_disable(struct mproc *); -void mproc_event_add(struct mproc *); -void m_compose(struct mproc *, uint32_t, uint32_t, pid_t, int, void *, size_t); -void m_composev(struct mproc *, uint32_t, uint32_t, pid_t, int, - const struct iovec *, int); -void m_forward(struct mproc *, struct imsg *); -void m_create(struct mproc *, uint32_t, uint32_t, pid_t, int); -void m_add(struct mproc *, const void *, size_t); -void m_add_int(struct mproc *, int); -void m_add_u32(struct mproc *, uint32_t); -void m_add_size(struct mproc *, size_t); -void m_add_time(struct mproc *, time_t); -void m_add_timeval(struct mproc *, struct timeval *tv); -void m_add_string(struct mproc *, const char *); -void m_add_data(struct mproc *, const void *, size_t); -void m_add_evpid(struct mproc *, uint64_t); -void m_add_msgid(struct mproc *, uint32_t); -void m_add_id(struct mproc *, uint64_t); -void m_add_sockaddr(struct mproc *, const struct sockaddr *); -void m_add_mailaddr(struct mproc *, const struct mailaddr *); -void m_add_envelope(struct mproc *, const struct envelope *); -void m_add_params(struct mproc *, struct dict *); -void m_close(struct mproc *); -void m_flush(struct mproc *); - -void m_msg(struct msg *, struct imsg *); -int m_is_eom(struct msg *); -void m_end(struct msg *); -void m_get_int(struct msg *, int *); -void m_get_size(struct msg *, size_t *); -void m_get_u32(struct msg *, uint32_t *); -void m_get_time(struct msg *, time_t *); -void m_get_timeval(struct msg *, struct timeval *); -void m_get_string(struct msg *, const char **); -void m_get_data(struct msg *, const void **, size_t *); -void m_get_evpid(struct msg *, uint64_t *); -void m_get_msgid(struct msg *, uint32_t *); -void m_get_id(struct msg *, uint64_t *); -void m_get_sockaddr(struct msg *, struct sockaddr *); -void m_get_mailaddr(struct msg *, struct mailaddr *); -void m_get_envelope(struct msg *, struct envelope *); -void m_get_params(struct msg *, struct dict *); -void m_clear_params(struct dict *); - - -/* mta.c */ -void mta_postfork(void); -void mta_postprivdrop(void); -void mta_imsg(struct mproc *, struct imsg *); -void mta_route_ok(struct mta_relay *, struct mta_route *); -void mta_route_error(struct mta_relay *, struct mta_route *); -void mta_route_down(struct mta_relay *, struct mta_route *); -void mta_route_collect(struct mta_relay *, struct mta_route *); -void mta_source_error(struct mta_relay *, struct mta_route *, const char *); -void mta_delivery_log(struct mta_envelope *, const char *, const char *, int, const char *); -void mta_delivery_notify(struct mta_envelope *); -struct mta_task *mta_route_next_task(struct mta_relay *, struct mta_route *); -const char *mta_host_to_text(struct mta_host *); -const char *mta_relay_to_text(struct mta_relay *); - - -/* mta_session.c */ -void mta_session(struct mta_relay *, struct mta_route *, const char *); -void mta_session_imsg(struct mproc *, struct imsg *); - - -/* parse.y */ -int parse_config(struct smtpd *, const char *, int); -int cmdline_symset(char *); - - -/* queue.c */ -int queue(void); - - -/* queue_backend.c */ -uint32_t queue_generate_msgid(void); -uint64_t queue_generate_evpid(uint32_t); -int queue_init(const char *, int); -int queue_close(void); -int queue_message_create(uint32_t *); -int queue_message_delete(uint32_t); -int queue_message_commit(uint32_t); -int queue_message_fd_r(uint32_t); -int queue_message_fd_rw(uint32_t); -int queue_envelope_create(struct envelope *); -int queue_envelope_delete(uint64_t); -int queue_envelope_load(uint64_t, struct envelope *); -int queue_envelope_update(struct envelope *); -int queue_envelope_walk(struct envelope *); -int queue_message_walk(struct envelope *, uint32_t, int *, void **); - - -/* report_smtp.c */ -void report_smtp_link_connect(const char *, uint64_t, const char *, int, - const struct sockaddr_storage *, const struct sockaddr_storage *); -void report_smtp_link_disconnect(const char *, uint64_t); -void report_smtp_link_greeting(const char *, uint64_t, const char *); -void report_smtp_link_identify(const char *, uint64_t, const char *, const char *); -void report_smtp_link_tls(const char *, uint64_t, const char *); -void report_smtp_link_auth(const char *, uint64_t, const char *, const char *); -void report_smtp_tx_reset(const char *, uint64_t, uint32_t); -void report_smtp_tx_begin(const char *, uint64_t, uint32_t); -void report_smtp_tx_mail(const char *, uint64_t, uint32_t, const char *, int); -void report_smtp_tx_rcpt(const char *, uint64_t, uint32_t, const char *, int); -void report_smtp_tx_envelope(const char *, uint64_t, uint32_t, uint64_t); -void report_smtp_tx_data(const char *, uint64_t, uint32_t, int); -void report_smtp_tx_commit(const char *, uint64_t, uint32_t, size_t); -void report_smtp_tx_rollback(const char *, uint64_t, uint32_t); -void report_smtp_protocol_client(const char *, uint64_t, const char *); -void report_smtp_protocol_server(const char *, uint64_t, const char *); -void report_smtp_filter_response(const char *, uint64_t, int, int, const char *); -void report_smtp_timeout(const char *, uint64_t); - - -/* ruleset.c */ -struct rule *ruleset_match(const struct envelope *); - - -/* scheduler.c */ -int scheduler(void); - - -/* scheduler_bakend.c */ -struct scheduler_backend *scheduler_backend_lookup(const char *); -void scheduler_info(struct scheduler_info *, struct envelope *); - - -/* pony.c */ -int pony(void); -void pony_imsg(struct mproc *, struct imsg *); - - -/* resolver.c */ -void resolver_getaddrinfo(const char *, const char *, const struct addrinfo *, - void(*)(void *, int, struct addrinfo*), void *); -void resolver_getnameinfo(const struct sockaddr *, int, - void(*)(void *, int, const char *, const char *), void *); -void resolver_res_query(const char *, int, int, - void (*cb)(void *, int, int, int, const void *, int), void *); -void resolver_dispatch_request(struct mproc *, struct imsg *); -void resolver_dispatch_result(struct mproc *, struct imsg *); - - -/* smtp.c */ -void smtp_postfork(void); -void smtp_postprivdrop(void); -void smtp_imsg(struct mproc *, struct imsg *); -void smtp_configure(void); -void smtp_collect(void); - - -/* smtp_session.c */ -int smtp_session(struct listener *, int, const struct sockaddr_storage *, - const char *, struct io *); -void smtp_session_imsg(struct mproc *, struct imsg *); - - -/* smtpf_session.c */ -int smtpf_session(struct listener *, int, const struct sockaddr_storage *, - const char *); -void smtpf_session_imsg(struct mproc *, struct imsg *); - - -/* smtpd.c */ -void imsg_dispatch(struct mproc *, struct imsg *); -const char *proc_name(enum smtp_proc_type); -const char *proc_title(enum smtp_proc_type); -const char *imsg_to_str(int); -void log_imsg(int, int, struct imsg *); -int fork_proc_backend(const char *, const char *, const char *); - - -/* srs.c */ -const char *srs_encode(const char *, const char *); -const char *srs_decode(const char *); - - -/* ssl_smtpd.c */ -void *ssl_mta_init(void *, char *, off_t, const char *); -void *ssl_smtp_init(void *, int); - - -/* stat_backend.c */ -struct stat_backend *stat_backend_lookup(const char *); -void stat_increment(const char *, size_t); -void stat_decrement(const char *, size_t); -void stat_set(const char *, const struct stat_value *); -struct stat_value *stat_counter(size_t); -struct stat_value *stat_timestamp(time_t); -struct stat_value *stat_timeval(struct timeval *); -struct stat_value *stat_timespec(struct timespec *); - - -/* table.c */ -struct table *table_find(struct smtpd *, const char *); -struct table *table_create(struct smtpd *, const char *, const char *, - const char *); -int table_config(struct table *); -int table_open(struct table *); -int table_update(struct table *); -void table_close(struct table *); -void table_dump(struct table *); -int table_check_use(struct table *, uint32_t, uint32_t); -int table_check_type(struct table *, uint32_t); -int table_check_service(struct table *, uint32_t); -int table_match(struct table *, enum table_service, const char *); -int table_lookup(struct table *, enum table_service, const char *, - union lookup *); -int table_fetch(struct table *, enum table_service, union lookup *); -void table_destroy(struct smtpd *, struct table *); -void table_add(struct table *, const char *, const char *); -int table_domain_match(const char *, const char *); -int table_netaddr_match(const char *, const char *); -int table_mailaddr_match(const char *, const char *); -int table_regex_match(const char *, const char *); -void table_open_all(struct smtpd *); -void table_dump_all(struct smtpd *); -void table_close_all(struct smtpd *); - - -/* to.c */ -int email_to_mailaddr(struct mailaddr *, char *); -int text_to_netaddr(struct netaddr *, const char *); -int text_to_mailaddr(struct mailaddr *, const char *); -int text_to_relayhost(struct relayhost *, const char *); -int text_to_userinfo(struct userinfo *, const char *); -int text_to_credentials(struct credentials *, const char *); -int text_to_expandnode(struct expandnode *, const char *); -uint64_t text_to_evpid(const char *); -uint32_t text_to_msgid(const char *); -const char *sa_to_text(const struct sockaddr *); -const char *ss_to_text(const struct sockaddr_storage *); -const char *time_to_text(time_t); -const char *duration_to_text(time_t); -const char *rule_to_text(struct rule *); -const char *sockaddr_to_text(struct sockaddr *); -const char *mailaddr_to_text(const struct mailaddr *); -const char *expandnode_to_text(struct expandnode *); - - -/* util.c */ -typedef struct arglist arglist; -struct arglist { - char **list; - uint num; - uint nalloc; -}; -void addargs(arglist *, char *, ...) - __attribute__((format(printf, 2, 3))); -int bsnprintf(char *, size_t, const char *, ...) - __attribute__((format (printf, 3, 4))); -int safe_fclose(FILE *); -int hostname_match(const char *, const char *); -int mailaddr_match(const struct mailaddr *, const struct mailaddr *); -int valid_localpart(const char *); -int valid_domainpart(const char *); -int valid_domainname(const char *); -int valid_smtp_response(const char *); -int secure_file(int, char *, char *, uid_t, int); -int lowercase(char *, const char *, size_t); -void xlowercase(char *, const char *, size_t); -int uppercase(char *, const char *, size_t); -uint64_t generate_uid(void); -int availdesc(void); -int ckdir(const char *, mode_t, uid_t, gid_t, int); -int rmtree(char *, int); -int mvpurge(char *, char *); -int mktmpfile(void); -const char *parse_smtp_response(char *, size_t, char **, int *); -int xasprintf(char **, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void *xmalloc(size_t); -void *xcalloc(size_t, size_t); -char *xstrdup(const char *); -void *xmemdup(const void *, size_t); -char *strip(char *); -int io_xprint(struct io *, const char *); -int io_xprintf(struct io *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void log_envelope(const struct envelope *, const char *, const char *, - const char *); -int session_socket_error(int); -int getmailname(char *, size_t); -int base64_encode(unsigned char const *, size_t, char *, size_t); -int base64_decode(char const *, unsigned char *, size_t); -int base64_encode_rfc3548(unsigned char const *, size_t, - char *, size_t); -void xclosefrom(int); - -void log_trace_verbose(int); -void log_trace(int, const char *, ...) - __attribute__((format (printf, 2, 3))); - -/* waitq.c */ -int waitq_wait(void *, void (*)(void *, void *, void *), void *); -void waitq_run(void *, void *); - - -/* runq.c */ -struct runq; - -int runq_init(struct runq **, void (*)(struct runq *, void *)); -int runq_schedule(struct runq *, time_t, void *); -int runq_schedule_at(struct runq *, time_t, void *); -int runq_cancel(struct runq *, void *); -int runq_pending(struct runq *, void *, time_t *); diff --git a/smtpd/smtpd/Makefile b/smtpd/smtpd/Makefile deleted file mode 100644 index 34a4dc74..00000000 --- a/smtpd/smtpd/Makefile +++ /dev/null @@ -1,102 +0,0 @@ -# $OpenBSD: Makefile,v 1.91 2018/06/03 14:04:06 gilles Exp $ - -.PATH: ${.CURDIR}/.. - -PROG= smtpd - -SRCS= aliases.c -SRCS+= bounce.c -SRCS+= ca.c -SRCS+= cert.c -SRCS+= compress_backend.c -SRCS+= config.c -SRCS+= control.c -SRCS+= crypto.c -SRCS+= dict.c -SRCS+= dns.c -SRCS+= unpack_dns.c -SRCS+= envelope.c -SRCS+= esc.c -SRCS+= expand.c -SRCS+= forward.c -SRCS+= iobuf.c -SRCS+= ioev.c -SRCS+= limit.c -SRCS+= lka.c -SRCS+= lka_filter.c -SRCS+= lka_session.c -SRCS+= log.c -SRCS+= mailaddr.c -SRCS+= mda.c -SRCS+= mda_mbox.c -SRCS+= mda_unpriv.c -SRCS+= mda_variables.c -SRCS+= mproc.c -SRCS+= mta.c -SRCS+= mta_session.c -SRCS+= parse.y -SRCS+= pony.c -SRCS+= proxy.c -SRCS+= queue.c -SRCS+= queue_backend.c -SRCS+= report_smtp.c -SRCS+= resolver.c -SRCS+= ruleset.c -SRCS+= runq.c -SRCS+= scheduler.c -SRCS+= scheduler_backend.c -SRCS+= smtp.c -SRCS+= smtp_session.c -SRCS+= smtpd.c -SRCS+= srs.c -SRCS+= ssl.c -SRCS+= ssl_smtpd.c -SRCS+= ssl_verify.c -SRCS+= stat_backend.c -SRCS+= table.c -SRCS+= to.c -SRCS+= tree.c -SRCS+= util.c -SRCS+= waitq.c - -# RFC parsers -SRCS+= rfc5322.c - -# backends -SRCS+= compress_gzip.c - -SRCS+= table_db.c -SRCS+= table_getpwnam.c -SRCS+= table_proc.c -SRCS+= table_static.c - -SRCS+= queue_fs.c -SRCS+= queue_null.c -SRCS+= queue_proc.c -SRCS+= queue_ram.c - -SRCS+= scheduler_ramqueue.c -SRCS+= scheduler_null.c -SRCS+= scheduler_proc.c - -SRCS+= stat_ramstat.c - -MAN= sendmail.8 smtpd.8 smtpd.conf.5 table.5 -BINDIR= /usr/sbin - -LDADD+= -levent -lutil -lssl -lcrypto -lm -lz -DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ} - -CFLAGS+= -fstack-protector-all -CFLAGS+= -I${.CURDIR}/.. -CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes -CFLAGS+= -Wmissing-declarations -CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual -CFLAGS+= -Wsign-compare -CFLAGS+= -Werror-implicit-function-declaration -#CFLAGS+= -Werror # during development phase (breaks some archs) -CFLAGS+= -DIO_TLS -CFLAGS+= -DQUEUE_PROFILING -YFLAGS= - -.include <bsd.prog.mk> diff --git a/smtpd/spfwalk.c b/smtpd/spfwalk.c deleted file mode 100644 index 0832d1bc..00000000 --- a/smtpd/spfwalk.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> - -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include <arpa/nameser_compat.h> -#endif -#include <arpa/inet.h> -#include <arpa/nameser.h> -#include <netinet/in.h> -#include <netdb.h> - -#include <asr.h> -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <unistd.h> - -#include "smtpd-defines.h" -#include "smtpd-api.h" -#include "unpack_dns.h" -#include "parser.h" - -struct target { - void (*dispatch)(struct dns_rr *, struct target *); - int cidr4; - int cidr6; -}; - -int spfwalk(int, struct parameter *); - -static void dispatch_txt(struct dns_rr *, struct target *); -static void dispatch_mx(struct dns_rr *, struct target *); -static void dispatch_a(struct dns_rr *, struct target *); -static void dispatch_aaaa(struct dns_rr *, struct target *); -static void lookup_record(int, const char *, struct target *); -static void dispatch_record(struct asr_result *, void *); -static ssize_t parse_txt(const char *, size_t, char *, size_t); -static int parse_target(char *, struct target *); -void *xmalloc(size_t size); - -int ip_v4 = 0; -int ip_v6 = 0; -int ip_both = 1; - -struct dict seen; - -int -spfwalk(int argc, struct parameter *argv) -{ - struct target tgt; - const char *ip_family = NULL; - char *line = NULL; - size_t linesize = 0; - ssize_t linelen; - - if (argv) - ip_family = argv[0].u.u_str; - - if (ip_family) { - if (strcmp(ip_family, "-4") == 0) { - ip_both = 0; - ip_v4 = 1; - } else if (strcmp(ip_family, "-6") == 0) { - ip_both = 0; - ip_v6 = 1; - } else - errx(1, "invalid ip_family"); - } - - dict_init(&seen); - event_init(); - - tgt.cidr4 = tgt.cidr6 = -1; - tgt.dispatch = dispatch_txt; - - while ((linelen = getline(&line, &linesize, stdin)) != -1) { - while (linelen-- > 0 && isspace((unsigned char)line[linelen])) - line[linelen] = '\0'; - - if (linelen > 0) - lookup_record(T_TXT, line, &tgt); - } - - free(line); - -#if HAVE_PLEDGE - if (pledge("dns stdio", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - - return 0; -} - -void -lookup_record(int type, const char *record, struct target *tgt) -{ - struct asr_query *as; - struct target *ntgt; - - as = res_query_async(record, C_IN, type, NULL); - if (as == NULL) - err(1, "res_query_async"); - ntgt = xmalloc(sizeof(*ntgt)); - *ntgt = *tgt; - event_asr_run(as, dispatch_record, (void *)ntgt); -} - -void -dispatch_record(struct asr_result *ar, void *arg) -{ - struct target *tgt = arg; - struct unpack pack; - struct dns_header h; - struct dns_query q; - struct dns_rr rr; - - /* best effort */ - if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) - goto end; - - unpack_init(&pack, ar->ar_data, ar->ar_datalen); - unpack_header(&pack, &h); - unpack_query(&pack, &q); - - for (; h.ancount; h.ancount--) { - unpack_rr(&pack, &rr); - /**/ - tgt->dispatch(&rr, tgt); - } -end: - free(tgt); -} - -void -dispatch_txt(struct dns_rr *rr, struct target *tgt) -{ - char buf[4096]; - char *argv[512]; - char buf2[512]; - struct target ltgt; - struct in6_addr ina; - char **ap = argv; - char *in = buf; - char *record, *end; - ssize_t n; - - if (rr->rr_type != T_TXT) - return; - n = parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen, buf, sizeof(buf)); - if (n == -1 || n == sizeof(buf)) - return; - buf[n] = '\0'; - - if (strncasecmp("v=spf1 ", buf, 7)) - return; - - while ((*ap = strsep(&in, " ")) != NULL) { - if (strcasecmp(*ap, "v=spf1") == 0) - continue; - - end = *ap + strlen(*ap)-1; - if (*end == '.') - *end = '\0'; - - if (dict_set(&seen, *ap, &seen)) - continue; - - if (**ap == '-' || **ap == '~') - continue; - - if (**ap == '+' || **ap == '?') - (*ap)++; - - ltgt.cidr4 = ltgt.cidr6 = -1; - - if (strncasecmp("ip4:", *ap, 4) == 0) { - if ((ip_v4 == 1 || ip_both == 1) && - inet_net_pton(AF_INET, *(ap) + 4, - &ina, sizeof(ina)) != -1) - printf("%s\n", *(ap) + 4); - continue; - } - if (strncasecmp("ip6:", *ap, 4) == 0) { - if ((ip_v6 == 1 || ip_both == 1) && - inet_net_pton(AF_INET6, *(ap) + 4, - &ina, sizeof(ina)) != -1) - printf("%s\n", *(ap) + 4); - continue; - } - if (strcasecmp("a", *ap) == 0) { - print_dname(rr->rr_dname, buf2, sizeof(buf2)); - buf2[strlen(buf2) - 1] = '\0'; - ltgt.dispatch = dispatch_a; - lookup_record(T_A, buf2, <gt); - ltgt.dispatch = dispatch_aaaa; - lookup_record(T_AAAA, buf2, <gt); - continue; - } - if (strncasecmp("a:", *ap, 2) == 0) { - record = *(ap) + 2; - if (parse_target(record, <gt) < 0) - continue; - ltgt.dispatch = dispatch_a; - lookup_record(T_A, record, <gt); - ltgt.dispatch = dispatch_aaaa; - lookup_record(T_AAAA, record, <gt); - continue; - } - if (strncasecmp("exists:", *ap, 7) == 0) { - ltgt.dispatch = dispatch_a; - lookup_record(T_A, *(ap) + 7, <gt); - continue; - } - if (strncasecmp("include:", *ap, 8) == 0) { - ltgt.dispatch = dispatch_txt; - lookup_record(T_TXT, *(ap) + 8, <gt); - continue; - } - if (strncasecmp("redirect=", *ap, 9) == 0) { - ltgt.dispatch = dispatch_txt; - lookup_record(T_TXT, *(ap) + 9, <gt); - continue; - } - if (strcasecmp("mx", *ap) == 0) { - print_dname(rr->rr_dname, buf2, sizeof(buf2)); - buf2[strlen(buf2) - 1] = '\0'; - ltgt.dispatch = dispatch_mx; - lookup_record(T_MX, buf2, <gt); - continue; - } - if (strncasecmp("mx:", *ap, 3) == 0) { - record = *(ap) + 3; - if (parse_target(record, <gt) < 0) - continue; - ltgt.dispatch = dispatch_mx; - lookup_record(T_MX, record, <gt); - continue; - } - } - *ap = NULL; -} - -void -dispatch_mx(struct dns_rr *rr, struct target *tgt) -{ - char buf[512]; - struct target ltgt; - - if (rr->rr_type != T_MX) - return; - - print_dname(rr->rr.mx.exchange, buf, sizeof(buf)); - buf[strlen(buf) - 1] = '\0'; - if (buf[strlen(buf) - 1] == '.') - buf[strlen(buf) - 1] = '\0'; - - ltgt = *tgt; - ltgt.dispatch = dispatch_a; - lookup_record(T_A, buf, <gt); - ltgt.dispatch = dispatch_aaaa; - lookup_record(T_AAAA, buf, <gt); -} - -void -dispatch_a(struct dns_rr *rr, struct target *tgt) -{ - char buffer[512]; - const char *ptr; - - if (rr->rr_type != T_A) - return; - - if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr, - buffer, sizeof buffer))) { - if (tgt->cidr4 >= 0) - printf("%s/%d\n", ptr, tgt->cidr4); - else - printf("%s\n", ptr); - } -} - -void -dispatch_aaaa(struct dns_rr *rr, struct target *tgt) -{ - char buffer[512]; - const char *ptr; - - if (rr->rr_type != T_AAAA) - return; - - if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6, - buffer, sizeof buffer))) { - if (tgt->cidr6 >= 0) - printf("%s/%d\n", ptr, tgt->cidr6); - else - printf("%s\n", ptr); - } -} - -ssize_t -parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz) -{ - size_t len; - ssize_t r = 0; - - while (rdatalen) { - len = *(const unsigned char *)rdata; - if (len >= rdatalen) { - errno = EINVAL; - return -1; - } - - rdata++; - rdatalen--; - - if (len == 0) - continue; - - if (len >= dstsz) { - errno = EOVERFLOW; - return -1; - } - memmove(dst, rdata, len); - dst += len; - dstsz -= len; - - rdata += len; - rdatalen -= len; - r += len; - } - - return r; -} - -int -parse_target(char *record, struct target *tgt) -{ - const char *err; - char *m4, *m6; - - m4 = record; - strsep(&m4, "/"); - if (m4 == NULL) - return 0; - - m6 = m4; - strsep(&m6, "/"); - - if (*m4) { - tgt->cidr4 = strtonum(m4, 0, 32, &err); - if (err) - return tgt->cidr4 = -1; - } - - if (m6 == NULL) - return 0; - - tgt->cidr6 = strtonum(m6, 0, 128, &err); - if (err) - return tgt->cidr6 = -1; - - return 0; -} diff --git a/smtpd/srs.c b/smtpd/srs.c deleted file mode 100644 index bb4f4d9e..00000000 --- a/smtpd/srs.c +++ /dev/null @@ -1,379 +0,0 @@ -/* $OpenBSD: srs.c,v 1.3 2019/09/29 10:03:49 gilles Exp $ */ - -/* - * Copyright (c) 2019 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <netdb.h> -#include <limits.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include <openssl/sha.h> - -#include "smtpd.h" -#include "log.h" - -static uint8_t base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - -static int -minrange(uint16_t tref, uint16_t t2, int drift, int mod) -{ - if (tref > drift) { - /* t2 must fall in between tref and tref - drift */ - if (t2 <= tref && t2>= tref - drift) - return 1; - } - else { - /* t2 must fall in between 0 and tref, or wrap */ - if (t2 <= tref || t2 >= mod - (drift - tref)) - return 1; - } - return 0; -} - -static int -maxrange(uint16_t tref, uint16_t t2, int drift, int mod) -{ - if (tref + drift < 1024) { - /* t2 must fall in between tref and tref + drift */ - if (t2 >= tref && t2 <= tref + drift) - return 1; - } - else { - /* t2 must fall in between tref + drift, or wrap */ - if (t2 >= tref || t2 <= (tref + drift) % 1024) - return 1; - } - return 0; -} - -static int -timestamp_check_range(uint16_t tref, uint16_t t2) -{ - if (! minrange(tref, t2, env->sc_srs_ttl, 1024) && - ! maxrange(tref, t2, 1, 1024)) - return 0; - - return 1; -} - -static const unsigned char * -srs_hash(const char *key, const char *value) -{ - SHA_CTX c; - static unsigned char md[SHA_DIGEST_LENGTH]; - - SHA1_Init(&c); - SHA1_Update(&c, key, strlen(key)); - SHA1_Update(&c, value, strlen(value)); - SHA1_Final(md, &c); - return md; -} - -static const char * -srs0_encode(const char *sender, const char *rcpt_domain) -{ - static char dest[SMTPD_MAXMAILADDRSIZE]; - char tmp[SMTPD_MAXMAILADDRSIZE]; - char md[SHA_DIGEST_LENGTH*4+1]; - struct mailaddr maddr; - uint16_t timestamp; - int ret; - - /* compute 10 bits timestamp according to spec */ - timestamp = (time(NULL) / (60 * 60 * 24)) % 1024; - - /* parse sender into user and domain */ - if (! text_to_mailaddr(&maddr, sender)) - return sender; - - /* TT=<orig_domainpart>=<orig_userpart>@<new_domainpart> */ - ret = snprintf(tmp, sizeof tmp, "%c%c=%s=%s@%s", - base32[(timestamp>>5) & 0x1F], - base32[timestamp & 0x1F], - maddr.domain, maddr.user, rcpt_domain); - if (ret == -1 || ret >= (int)sizeof tmp) - return sender; - - /* compute HHHH */ - base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH, - md, sizeof md); - - /* prepend SRS0=HHHH= prefix */ - ret = snprintf(dest, sizeof dest, "SRS0=%c%c%c%c=%s", - md[0], md[1], md[2], md[3], tmp); - if (ret == -1 || ret >= (int)sizeof dest) - return sender; - - return dest; -} - -static const char * -srs1_encode_srs0(const char *sender, const char *rcpt_domain) -{ - static char dest[SMTPD_MAXMAILADDRSIZE]; - char tmp[SMTPD_MAXMAILADDRSIZE]; - char md[SHA_DIGEST_LENGTH*4+1]; - struct mailaddr maddr; - int ret; - - /* parse sender into user and domain */ - if (! text_to_mailaddr(&maddr, sender)) - return sender; - - /* <last_domainpart>==<SRS0_userpart>@<new_domainpart> */ - ret = snprintf(tmp, sizeof tmp, "%s==%s@%s", - maddr.domain, maddr.user, rcpt_domain); - if (ret == -1 || ret >= (int)sizeof tmp) - return sender; - - /* compute HHHH */ - base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH, - md, sizeof md); - - /* prepend SRS1=HHHH= prefix */ - ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s", - md[0], md[1], md[2], md[3], tmp); - if (ret == -1 || ret >= (int)sizeof dest) - return sender; - - return dest; -} - -static const char * -srs1_encode_srs1(const char *sender, const char *rcpt_domain) -{ - static char dest[SMTPD_MAXMAILADDRSIZE]; - char tmp[SMTPD_MAXMAILADDRSIZE]; - char md[SHA_DIGEST_LENGTH*4+1]; - struct mailaddr maddr; - int ret; - - /* parse sender into user and domain */ - if (! text_to_mailaddr(&maddr, sender)) - return sender; - - /* <SRS1_userpart>@<new_domainpart> */ - ret = snprintf(tmp, sizeof tmp, "%s@%s", maddr.user, rcpt_domain); - if (ret == -1 || ret >= (int)sizeof tmp) - return sender; - - /* sanity check: there's at least room for a checksum - * with allowed delimiter =, + or - - */ - if (strlen(tmp) < 5) - return sender; - if (tmp[4] != '=' && tmp[4] != '+' && tmp[4] != '-') - return sender; - - /* compute HHHH */ - base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp + 5), SHA_DIGEST_LENGTH, - md, sizeof md); - - /* prepend SRS1=HHHH= prefix skipping previous hops' HHHH */ - ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s", - md[0], md[1], md[2], md[3], tmp + 5); - if (ret == -1 || ret >= (int)sizeof dest) - return sender; - - return dest; -} - -const char * -srs_encode(const char *sender, const char *rcpt_domain) -{ - if (strncasecmp(sender, "SRS0=", 5) == 0) - return srs1_encode_srs0(sender+5, rcpt_domain); - if (strncasecmp(sender, "SRS1=", 5) == 0) - return srs1_encode_srs1(sender+5, rcpt_domain); - return srs0_encode(sender, rcpt_domain); -} - -static const char * -srs0_decode(const char *rcpt) -{ - static char dest[SMTPD_MAXMAILADDRSIZE]; - char md[SHA_DIGEST_LENGTH*4+1]; - struct mailaddr maddr; - char *p; - uint8_t *idx; - int ret; - uint16_t timestamp, srs_timestamp; - - /* sanity check: we have room for a checksum and delimiter */ - if (strlen(rcpt) < 5) - return NULL; - - /* compute checksum */ - base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH, - md, sizeof md); - - /* compare prefix checksum with computed checksum */ - if (strncmp(md, rcpt, 4) != 0) { - if (env->sc_srs_key_backup == NULL) - return NULL; - base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5), - SHA_DIGEST_LENGTH, md, sizeof md); - if (strncmp(md, rcpt, 4) != 0) - return NULL; - } - rcpt += 5; - - /* sanity check: we have room for a timestamp and delimiter */ - if (strlen(rcpt) < 3) - return NULL; - - /* decode timestamp */ - if ((idx = strchr(base32, rcpt[0])) == NULL) - return NULL; - srs_timestamp = ((idx - base32) << 5); - - if ((idx = strchr(base32, rcpt[1])) == NULL) - return NULL; - srs_timestamp |= (idx - base32); - rcpt += 3; - - /* compute current 10 bits timestamp */ - timestamp = (time(NULL) / (60 * 60 * 24)) % 1024; - - /* check that SRS timestamp isn't too far from current */ - if (timestamp != srs_timestamp) - if (! timestamp_check_range(timestamp, srs_timestamp)) - return NULL; - - if (! text_to_mailaddr(&maddr, rcpt)) - return NULL; - - /* sanity check: we have at least one SRS separator */ - if ((p = strchr(maddr.user, '=')) == NULL) - return NULL; - *p++ = '\0'; - - /* maddr.user holds "domain\0user", with p pointing at user */ - ret = snprintf(dest, sizeof dest, "%s@%s", p, maddr.user); - if (ret == -1 || ret >= (int)sizeof dest) - return NULL; - - return dest; -} - -static const char * -srs1_decode(const char *rcpt) -{ - static char dest[SMTPD_MAXMAILADDRSIZE]; - char md[SHA_DIGEST_LENGTH*4+1]; - struct mailaddr maddr; - char *p; - uint8_t *idx; - int ret; - uint16_t timestamp, srs_timestamp; - - /* sanity check: we have room for a checksum and delimiter */ - if (strlen(rcpt) < 5) - return NULL; - - /* compute checksum */ - base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH, - md, sizeof md); - - /* compare prefix checksum with computed checksum */ - if (strncmp(md, rcpt, 4) != 0) { - if (env->sc_srs_key_backup == NULL) - return NULL; - base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5), - SHA_DIGEST_LENGTH, md, sizeof md); - if (strncmp(md, rcpt, 4) != 0) - return NULL; - } - rcpt += 5; - - if (! text_to_mailaddr(&maddr, rcpt)) - return NULL; - - /* sanity check: we have at least one SRS separator */ - if ((p = strchr(maddr.user, '=')) == NULL) - return NULL; - *p++ = '\0'; - - /* maddr.user holds "domain\0user", with p pointing at user */ - ret = snprintf(dest, sizeof dest, "SRS0%s@%s", p, maddr.user); - if (ret == -1 || ret >= (int)sizeof dest) - return NULL; - - - /* we're ready to return decoded address, but let's check if - * SRS0 timestamp is valid. - */ - - /* first, get rid of SRS0 checksum (=HHHH=), we can't check it */ - if (strlen(p) < 6) - return NULL; - p += 6; - - /* we should be pointing to a timestamp, check that we're indeed */ - if (strlen(p) < 3) - return NULL; - if (p[2] != '=' && p[2] != '+' && p[2] != '-') - return NULL; - p[2] = '\0'; - - if ((idx = strchr(base32, p[0])) == NULL) - return NULL; - srs_timestamp = ((idx - base32) << 5); - - if ((idx = strchr(base32, p[1])) == NULL) - return NULL; - srs_timestamp |= (idx - base32); - - /* compute current 10 bits timestamp */ - timestamp = (time(NULL) / (60 * 60 * 24)) % 1024; - - /* check that SRS timestamp isn't too far from current */ - if (timestamp != srs_timestamp) - if (! timestamp_check_range(timestamp, srs_timestamp)) - return NULL; - - return dest; -} - -const char * -srs_decode(const char *rcpt) -{ - if (strncasecmp(rcpt, "SRS0=", 5) == 0) - return srs0_decode(rcpt + 5); - if (strncasecmp(rcpt, "SRS1=", 5) == 0) - return srs1_decode(rcpt + 5); - - return NULL; -} diff --git a/smtpd/ssl.c b/smtpd/ssl.c deleted file mode 100644 index a37d6fea..00000000 --- a/smtpd/ssl.c +++ /dev/null @@ -1,458 +0,0 @@ -/* $OpenBSD: ssl.c,v 1.93 2019/06/05 06:40:13 gilles Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <limits.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <openssl/ssl.h> -#include <openssl/engine.h> -#include <openssl/err.h> -#include <openssl/rsa.h> -#include <openssl/ecdsa.h> -#include <openssl/dh.h> -#include <openssl/bn.h> - -#include "log.h" -#include "ssl.h" - -void -ssl_init(void) -{ - static int inited = 0; - - if (inited) - return; - - SSL_library_init(); - SSL_load_error_strings(); - - OpenSSL_add_all_algorithms(); - - /* Init hardware crypto engines. */ - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - inited = 1; -} - -int -ssl_setup(SSL_CTX **ctxp, struct pki *pki, - int (*sni_cb)(SSL *,int *,void *), const char *ciphers) -{ - SSL_CTX *ctx; - uint8_t sid[SSL_MAX_SID_CTX_LENGTH]; - - ctx = ssl_ctx_create(pki->pki_name, pki->pki_cert, pki->pki_cert_len, ciphers); - - /* - * Set session ID context to a random value. We don't support - * persistent caching of sessions so it is OK to set a temporary - * session ID context that is valid during run time. - */ - arc4random_buf(sid, sizeof(sid)); - if (!SSL_CTX_set_session_id_context(ctx, sid, sizeof(sid))) - goto err; - - if (sni_cb) - SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb); - - SSL_CTX_set_dh_auto(ctx, 0); - - SSL_CTX_set_ecdh_auto(ctx, 1); - - *ctxp = ctx; - return 1; - -err: - SSL_CTX_free(ctx); - ssl_error("ssl_setup"); - return 0; -} - -char * -ssl_load_file(const char *name, off_t *len, mode_t perm) -{ - struct stat st; - off_t size; - char *buf = NULL; - int fd, saved_errno; - char mode[12]; - - if ((fd = open(name, O_RDONLY)) == -1) - return (NULL); - if (fstat(fd, &st) != 0) - goto fail; - if (st.st_uid != 0) { - log_warnx("warn: %s: not owned by uid 0", name); - errno = EACCES; - goto fail; - } - if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) { - strmode(perm, mode); - log_warnx("warn: %s: insecure permissions: must be at most %s", - name, &mode[1]); - errno = EACCES; - goto fail; - } - size = st.st_size; - if ((buf = calloc(1, size + 1)) == NULL) - goto fail; - if (read(fd, buf, size) != size) - goto fail; - close(fd); - - *len = size + 1; - return (buf); - -fail: - free(buf); - saved_errno = errno; - close(fd); - errno = saved_errno; - return (NULL); -} - -#if 0 -static int -ssl_password_cb(char *buf, int size, int rwflag, void *u) -{ - size_t len; - if (u == NULL) { - explicit_bzero(buf, size); - return (0); - } - if ((len = strlcpy(buf, u, size)) >= (size_t)size) - return (0); - return (len); -} -#endif - -static int -ssl_password_cb(char *buf, int size, int rwflag, void *u) -{ - int ret = 0; - size_t len; - char *pass; - - pass = getpass((const char *)u); - if (pass == NULL) - return 0; - len = strlen(pass); - if (strlcpy(buf, pass, size) >= (size_t)size) - goto end; - ret = len; -end: - if (len) - explicit_bzero(pass, len); - return ret; -} - -char * -ssl_load_key(const char *name, off_t *len, char *pass, mode_t perm, const char *pkiname) -{ - FILE *fp = NULL; - EVP_PKEY *key = NULL; - BIO *bio = NULL; - long size; - char *data, *buf, *filebuf; - struct stat st; - char mode[12]; - char prompt[2048]; - - /* Initialize SSL library once */ - ssl_init(); - - /* - * Read (possibly) encrypted key from file - */ - if ((fp = fopen(name, "r")) == NULL) - return (NULL); - if ((filebuf = malloc_conceal(BUFSIZ)) == NULL) - goto fail; - setvbuf(fp, filebuf, _IOFBF, BUFSIZ); - - if (fstat(fileno(fp), &st) != 0) - goto fail; - if (st.st_uid != 0) { - log_warnx("warn: %s: not owned by uid 0", name); - errno = EACCES; - goto fail; - } - if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) { - strmode(perm, mode); - log_warnx("warn: %s: insecure permissions: must be at most %s", - name, &mode[1]); - errno = EACCES; - goto fail; - } - - (void)snprintf(prompt, sizeof prompt, "passphrase for %s: ", pkiname); - key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, prompt); - fclose(fp); - fp = NULL; - freezero(filebuf, BUFSIZ); - filebuf = NULL; - if (key == NULL) - goto fail; - /* - * Write unencrypted key to memory buffer - */ - if ((bio = BIO_new(BIO_s_mem())) == NULL) - goto fail; - if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) - goto fail; - if ((size = BIO_get_mem_data(bio, &data)) <= 0) - goto fail; - if ((buf = calloc_conceal(1, size + 1)) == NULL) - goto fail; - memcpy(buf, data, size); - - BIO_free_all(bio); - EVP_PKEY_free(key); - - *len = (off_t)size + 1; - return (buf); - -fail: - ssl_error("ssl_load_key"); - BIO_free_all(bio); - EVP_PKEY_free(key); - if (fp) - fclose(fp); - freezero(filebuf, BUFSIZ); - return (NULL); -} - -SSL_CTX * -ssl_ctx_create(const char *pkiname, char *cert, off_t cert_len, const char *ciphers) -{ - SSL_CTX *ctx; - size_t pkinamelen = 0; - - ctx = SSL_CTX_new(SSLv23_method()); - if (ctx == NULL) { - ssl_error("ssl_ctx_create"); - fatal("ssl_ctx_create: could not create SSL context"); - } - - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - SSL_CTX_set_timeout(ctx, SSL_SESSION_TIMEOUT); - SSL_CTX_set_options(ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TICKET); - SSL_CTX_set_options(ctx, - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION); - SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - - if (ciphers == NULL) - ciphers = SSL_CIPHERS; - if (!SSL_CTX_set_cipher_list(ctx, ciphers)) { - ssl_error("ssl_ctx_create"); - fatal("ssl_ctx_create: could not set cipher list"); - } - - if (cert != NULL) { - if (pkiname != NULL) - pkinamelen = strlen(pkiname) + 1; - if (!SSL_CTX_use_certificate_chain_mem(ctx, cert, cert_len)) { - ssl_error("ssl_ctx_create"); - fatal("ssl_ctx_create: invalid certificate chain"); - } else if (!ssl_ctx_fake_private_key(ctx, - pkiname, pkinamelen, cert, cert_len, NULL, NULL)) { - ssl_error("ssl_ctx_create"); - fatal("ssl_ctx_create: could not fake private key"); - } else if (!SSL_CTX_check_private_key(ctx)) { - ssl_error("ssl_ctx_create"); - fatal("ssl_ctx_create: invalid private key"); - } - } - - return (ctx); -} - -int -ssl_load_certificate(struct pki *p, const char *pathname) -{ - p->pki_cert = ssl_load_file(pathname, &p->pki_cert_len, 0755); - if (p->pki_cert == NULL) - return 0; - return 1; -} - -int -ssl_load_keyfile(struct pki *p, const char *pathname, const char *pkiname) -{ - char pass[1024]; - - p->pki_key = ssl_load_key(pathname, &p->pki_key_len, pass, 0740, pkiname); - if (p->pki_key == NULL) - return 0; - return 1; -} - -int -ssl_load_cafile(struct ca *c, const char *pathname) -{ - c->ca_cert = ssl_load_file(pathname, &c->ca_cert_len, 0755); - if (c->ca_cert == NULL) - return 0; - return 1; -} - -const char * -ssl_to_text(const SSL *ssl) -{ - static char buf[256]; - - (void)snprintf(buf, sizeof buf, "%s:%s:%d", - SSL_get_version(ssl), - SSL_get_cipher_name(ssl), - SSL_get_cipher_bits(ssl, NULL)); - - return (buf); -} - -void -ssl_error(const char *where) -{ - unsigned long code; - char errbuf[128]; - - for (; (code = ERR_get_error()) != 0 ;) { - ERR_error_string_n(code, errbuf, sizeof(errbuf)); - log_debug("debug: SSL library error: %s: %s", where, errbuf); - } -} - -int -ssl_load_pkey(const void *data, size_t datalen, char *buf, off_t len, - X509 **x509ptr, EVP_PKEY **pkeyptr) -{ - BIO *in; - X509 *x509 = NULL; - EVP_PKEY *pkey = NULL; - RSA *rsa = NULL; - EC_KEY *eckey = NULL; - void *exdata = NULL; - - if ((in = BIO_new_mem_buf(buf, len)) == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_BUF_LIB); - return (0); - } - - if ((x509 = PEM_read_bio_X509(in, NULL, - ssl_password_cb, NULL)) == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_PEM_LIB); - goto fail; - } - - if ((pkey = X509_get_pubkey(x509)) == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_X509_LIB); - goto fail; - } - - BIO_free(in); - in = NULL; - - if (data != NULL && datalen) { - if (((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL && - (eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) || - (exdata = malloc(datalen)) == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_EVP_LIB); - goto fail; - } - - memcpy(exdata, data, datalen); - if (rsa) - RSA_set_ex_data(rsa, 0, exdata); -#if defined(SUPPORT_ECDSA) - if (eckey) - ECDSA_set_ex_data(eckey, 0, exdata); -#endif - RSA_free(rsa); /* dereference, will be cleaned up with pkey */ -#if defined(SUPPORT_ECDSA) - EC_KEY_free(eckey); /* dereference, will be cleaned up with pkey */ -#endif - } - - *x509ptr = x509; - *pkeyptr = pkey; - - return (1); - - fail: - RSA_free(rsa); - EC_KEY_free(eckey); - BIO_free(in); - EVP_PKEY_free(pkey); - X509_free(x509); - free(exdata); - - return (0); -} - -int -ssl_ctx_fake_private_key(SSL_CTX *ctx, const void *data, size_t datalen, - char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr) -{ - int ret = 0; - EVP_PKEY *pkey = NULL; - X509 *x509 = NULL; - - if (!ssl_load_pkey(data, datalen, buf, len, &x509, &pkey)) - return (0); - - /* - * Use the public key as the "private" key - the secret key - * parameters are hidden in an extra process that will be - * contacted by the RSA engine. The SSL/TLS library needs at - * least the public key parameters in the current process. - */ - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - if (!ret) - SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_LIB_SSL); - - if (pkeyptr != NULL) - *pkeyptr = pkey; - else - EVP_PKEY_free(pkey); - - if (x509ptr != NULL) - *x509ptr = x509; - else - X509_free(x509); - - return (ret); -} diff --git a/smtpd/ssl.h b/smtpd/ssl.h deleted file mode 100644 index 11c80c68..00000000 --- a/smtpd/ssl.h +++ /dev/null @@ -1,71 +0,0 @@ -/* $OpenBSD: ssl.h,v 1.21 2019/09/18 11:26:30 eric Exp $ */ -/* - * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define SSL_CIPHERS "HIGH:!aNULL:!MD5" -#define SSL_SESSION_TIMEOUT 300 - -struct pki { - char pki_name[HOST_NAME_MAX+1]; - - char *pki_cert_file; - char *pki_cert; - off_t pki_cert_len; - - char *pki_key_file; - char *pki_key; - off_t pki_key_len; - - EVP_PKEY *pki_pkey; - - int pki_dhe; -}; - -struct ca { - char ca_name[HOST_NAME_MAX+1]; - - char *ca_cert_file; - char *ca_cert; - off_t ca_cert_len; -}; - - -/* ssl.c */ -void ssl_init(void); -int ssl_setup(SSL_CTX **, struct pki *, - int (*)(SSL *, int *, void *), const char *); -SSL_CTX *ssl_ctx_create(const char *, char *, off_t, const char *); -int ssl_cmp(struct pki *, struct pki *); -char *ssl_load_file(const char *, off_t *, mode_t); -char *ssl_load_key(const char *, off_t *, char *, mode_t, const char *); - -const char *ssl_to_text(const SSL *); -void ssl_error(const char *); - -int ssl_load_certificate(struct pki *, const char *); -int ssl_load_keyfile(struct pki *, const char *, const char *); -int ssl_load_cafile(struct ca *, const char *); -int ssl_load_pkey(const void *, size_t, char *, off_t, - X509 **, EVP_PKEY **); -int ssl_ctx_fake_private_key(SSL_CTX *, const void *, size_t, - char *, off_t, X509 **, EVP_PKEY **); - -/* ssl_privsep.c */ -int ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **); -int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int); - -/* ssl_verify.c */ -int ssl_check_name(X509 *, const char *, int *); diff --git a/smtpd/ssl_smtpd.c b/smtpd/ssl_smtpd.c deleted file mode 100644 index 4e5b7e75..00000000 --- a/smtpd/ssl_smtpd.c +++ /dev/null @@ -1,105 +0,0 @@ -/* $OpenBSD: ssl_smtpd.c,v 1.13 2015/12/30 16:02:08 benno Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <event.h> -#include <fcntl.h> -#include <limits.h> -#include <imsg.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <openssl/ssl.h> -#include <openssl/engine.h> -#include <openssl/err.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - - -void * -ssl_mta_init(void *pkiname, char *cert, off_t cert_len, const char *ciphers) -{ - SSL_CTX *ctx = NULL; - SSL *ssl = NULL; - - ctx = ssl_ctx_create(pkiname, cert, cert_len, ciphers); - - if ((ssl = SSL_new(ctx)) == NULL) - goto err; - if (!SSL_set_ssl_method(ssl, SSLv23_client_method())) - goto err; - - SSL_CTX_free(ctx); - return (void *)(ssl); - -err: - SSL_free(ssl); - SSL_CTX_free(ctx); - ssl_error("ssl_mta_init"); - return (NULL); -} - -/* dummy_verify */ -static int -dummy_verify(int ok, X509_STORE_CTX *store) -{ - /* - * We *want* SMTP to request an optional client certificate, however we don't want the - * verification to take place in the SMTP process. This dummy verify will allow us to - * asynchronously verify in the lookup process. - */ - return 1; -} - -void * -ssl_smtp_init(void *ssl_ctx, int verify) -{ - SSL *ssl = NULL; - - log_debug("debug: session_start_ssl: switching to SSL"); - - if (verify) - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, dummy_verify); - - if ((ssl = SSL_new(ssl_ctx)) == NULL) - goto err; - if (!SSL_set_ssl_method(ssl, SSLv23_server_method())) - goto err; - - return (void *)(ssl); - -err: - SSL_free(ssl); - ssl_error("ssl_smtp_init"); - return (NULL); -} diff --git a/smtpd/ssl_verify.c b/smtpd/ssl_verify.c deleted file mode 100644 index 2e784b97..00000000 --- a/smtpd/ssl_verify.c +++ /dev/null @@ -1,297 +0,0 @@ -/* $OpenBSD: ssl_verify.c,v 1.2 2019/11/02 03:16:45 gilles Exp $ */ -/* - * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* Adapted from lib/libtls/tls_verify.c */ - -#include "includes.h" - -#include <sys/socket.h> - -#include <arpa/inet.h> -#include <netinet/in.h> - -#include <limits.h> -#include <string.h> - -#include <openssl/x509v3.h> - -#if 0 -#include <tls.h> -#include "tls_internal.h" -#endif - -#include "ssl.h" -#include "log.h" - -struct tls; -#define tls_set_errorx(ctx, ...) log_warnx(__VA_ARGS__) -union tls_addr { - struct in_addr in; - struct in6_addr in6; -}; - -static int -tls_match_name(const char *cert_name, const char *name) -{ - const char *cert_domain, *domain, *next_dot; - - if (strcasecmp(cert_name, name) == 0) - return 0; - - /* Wildcard match? */ - if (cert_name[0] == '*') { - /* - * Valid wildcards: - * - "*.domain.tld" - * - "*.sub.domain.tld" - * - etc. - * Reject "*.tld". - * No attempt to prevent the use of eg. "*.co.uk". - */ - cert_domain = &cert_name[1]; - /* Disallow "*" */ - if (cert_domain[0] == '\0') - return -1; - /* Disallow "*foo" */ - if (cert_domain[0] != '.') - return -1; - /* Disallow "*.." */ - if (cert_domain[1] == '.') - return -1; - next_dot = strchr(&cert_domain[1], '.'); - /* Disallow "*.bar" */ - if (next_dot == NULL) - return -1; - /* Disallow "*.bar.." */ - if (next_dot[1] == '.') - return -1; - - domain = strchr(name, '.'); - - /* No wildcard match against a name with no host part. */ - if (name[0] == '.') - return -1; - /* No wildcard match against a name with no domain part. */ - if (domain == NULL || strlen(domain) == 1) - return -1; - - if (strcasecmp(cert_domain, domain) == 0) - return 0; - } - - return -1; -} - -/* - * See RFC 5280 section 4.2.1.6 for SubjectAltName details. - * alt_match is set to 1 if a matching alternate name is found. - * alt_exists is set to 1 if any known alternate name exists in the certificate. - */ -static int -tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name, - int *alt_match, int *alt_exists) -{ - STACK_OF(GENERAL_NAME) *altname_stack = NULL; - union tls_addr addrbuf; - int addrlen, type; - int count, i; - int rv = 0; - - *alt_match = 0; - *alt_exists = 0; - - altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, - NULL, NULL); - if (altname_stack == NULL) - return 0; - - if (inet_pton(AF_INET, name, &addrbuf) == 1) { - type = GEN_IPADD; - addrlen = 4; - } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { - type = GEN_IPADD; - addrlen = 16; - } else { - type = GEN_DNS; - addrlen = 0; - } - - count = sk_GENERAL_NAME_num(altname_stack); - for (i = 0; i < count; i++) { - GENERAL_NAME *altname; - - altname = sk_GENERAL_NAME_value(altname_stack, i); - - if (altname->type == GEN_DNS || altname->type == GEN_IPADD) - *alt_exists = 1; - - if (altname->type != type) - continue; - - if (type == GEN_DNS) { - const unsigned char *data; - int format, len; - - format = ASN1_STRING_type(altname->d.dNSName); - if (format == V_ASN1_IA5STRING) { - data = ASN1_STRING_get0_data(altname->d.dNSName); - len = ASN1_STRING_length(altname->d.dNSName); - - if (len < 0 || (size_t)len != strlen(data)) { - tls_set_errorx(ctx, - "error verifying name '%s': " - "NUL byte in subjectAltName, " - "probably a malicious certificate", - name); - rv = -1; - break; - } - - /* - * Per RFC 5280 section 4.2.1.6: - * " " is a legal domain name, but that - * dNSName must be rejected. - */ - if (strcmp(data, " ") == 0) { - tls_set_errorx(ctx, - "error verifying name '%s': " - "a dNSName of \" \" must not be " - "used", name); - rv = -1; - break; - } - - if (tls_match_name(data, name) == 0) { - *alt_match = 1; - break; - } - } else { -#ifdef DEBUG - fprintf(stdout, "%s: unhandled subjectAltName " - "dNSName encoding (%d)\n", getprogname(), - format); -#endif - } - - } else if (type == GEN_IPADD) { - const unsigned char *data; - int datalen; - - datalen = ASN1_STRING_length(altname->d.iPAddress); - data = ASN1_STRING_get0_data(altname->d.iPAddress); - - if (datalen < 0) { - tls_set_errorx(ctx, - "Unexpected negative length for an " - "IP address: %d", datalen); - rv = -1; - break; - } - - /* - * Per RFC 5280 section 4.2.1.6: - * IPv4 must use 4 octets and IPv6 must use 16 octets. - */ - if (datalen == addrlen && - memcmp(data, &addrbuf, addrlen) == 0) { - *alt_match = 1; - break; - } - } - } - - sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); - return rv; -} - -static int -tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, - int *cn_match) -{ - X509_NAME *subject_name; - char *common_name = NULL; - union tls_addr addrbuf; - int common_name_len; - int rv = 0; - - *cn_match = 0; - - subject_name = X509_get_subject_name(cert); - if (subject_name == NULL) - goto done; - - common_name_len = X509_NAME_get_text_by_NID(subject_name, - NID_commonName, NULL, 0); - if (common_name_len < 0) - goto done; - - common_name = calloc(common_name_len + 1, 1); - if (common_name == NULL) - goto done; - - X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name, - common_name_len + 1); - - /* NUL bytes in CN? */ - if (common_name_len < 0 || - (size_t)common_name_len != strlen(common_name)) { - tls_set_errorx(ctx, "error verifying name '%s': " - "NUL byte in Common Name field, " - "probably a malicious certificate", name); - rv = -1; - goto done; - } - - /* - * We don't want to attempt wildcard matching against IP addresses, - * so perform a simple comparison here. - */ - if (inet_pton(AF_INET, name, &addrbuf) == 1 || - inet_pton(AF_INET6, name, &addrbuf) == 1) { - if (strcmp(common_name, name) == 0) - *cn_match = 1; - goto done; - } - - if (tls_match_name(common_name, name) == 0) - *cn_match = 1; - - done: - free(common_name); - return rv; -} - -int -ssl_check_name(X509 *cert, const char *name, int *match) -{ - int alt_exists; - - *match = 0; - - if (tls_check_subject_altname(NULL, cert, name, match, - &alt_exists) == -1) - return -1; - - /* - * As per RFC 6125 section 6.4.4, if any known alternate name existed - * in the certificate, we do not attempt to match on the CN. - */ - if (*match || alt_exists) - return 0; - - return tls_check_common_name(NULL, cert, name, match); -} diff --git a/smtpd/stat_backend.c b/smtpd/stat_backend.c deleted file mode 100644 index 30cb299b..00000000 --- a/smtpd/stat_backend.c +++ /dev/null @@ -1,124 +0,0 @@ -/* $OpenBSD: stat_backend.c,v 1.11 2018/12/27 10:35:26 gilles Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <sys/tree.h> - -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <string.h> -#include <limits.h> - -#include "log.h" -#include "smtpd.h" - -extern struct stat_backend stat_backend_ramstat; - -struct stat_backend * -stat_backend_lookup(const char *name) -{ - return &stat_backend_ramstat; -} - -void -stat_increment(const char *key, size_t count) -{ - struct stat_value *value; - - if (count == 0) - return; - - value = stat_counter(count); - - m_create(p_control, IMSG_STAT_INCREMENT, 0, 0, -1); - m_add_string(p_control, key); - m_add_data(p_control, value, sizeof(*value)); - m_close(p_control); -} - -void -stat_decrement(const char *key, size_t count) -{ - struct stat_value *value; - - if (count == 0) - return; - - value = stat_counter(count); - - m_create(p_control, IMSG_STAT_DECREMENT, 0, 0, -1); - m_add_string(p_control, key); - m_add_data(p_control, value, sizeof(*value)); - m_close(p_control); -} - -void -stat_set(const char *key, const struct stat_value *value) -{ - m_create(p_control, IMSG_STAT_SET, 0, 0, -1); - m_add_string(p_control, key); - m_add_data(p_control, value, sizeof(*value)); - m_close(p_control); -} - -/* helpers */ - -struct stat_value * -stat_counter(size_t counter) -{ - static struct stat_value value; - - value.type = STAT_COUNTER; - value.u.counter = counter; - return &value; -} - -struct stat_value * -stat_timestamp(time_t timestamp) -{ - static struct stat_value value; - - value.type = STAT_TIMESTAMP; - value.u.timestamp = timestamp; - return &value; -} - -struct stat_value * -stat_timeval(struct timeval *tv) -{ - static struct stat_value value; - - value.type = STAT_TIMEVAL; - value.u.tv = *tv; - return &value; -} - -struct stat_value * -stat_timespec(struct timespec *ts) -{ - static struct stat_value value; - - value.type = STAT_TIMESPEC; - value.u.ts = *ts; - return &value; -} diff --git a/smtpd/stat_ramstat.c b/smtpd/stat_ramstat.c deleted file mode 100644 index bbf1541a..00000000 --- a/smtpd/stat_ramstat.c +++ /dev/null @@ -1,162 +0,0 @@ -/* $OpenBSD: stat_ramstat.c,v 1.11 2018/05/31 21:06:12 gilles Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <sys/tree.h> - -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - - -static void ramstat_init(void); -static void ramstat_close(void); -static void ramstat_increment(const char *, size_t); -static void ramstat_decrement(const char *, size_t); -static void ramstat_set(const char *, const struct stat_value *); -static int ramstat_iter(void **, char **, struct stat_value *); - -struct ramstat_entry { - RB_ENTRY(ramstat_entry) entry; - char key[STAT_KEY_SIZE]; - struct stat_value value; -}; -RB_HEAD(stats_tree, ramstat_entry) stats; -RB_PROTOTYPE(stats_tree, ramstat_entry, entry, ramstat_entry_cmp); - -struct stat_backend stat_backend_ramstat = { - ramstat_init, - ramstat_close, - ramstat_increment, - ramstat_decrement, - ramstat_set, - ramstat_iter -}; - -static void -ramstat_init(void) -{ - log_trace(TRACE_STAT, "ramstat: init"); - - RB_INIT(&stats); - - /* ramstat_set() should be called for each key we want - * to have displayed by smtpctl show stats at startup. - */ - ramstat_set("uptime", stat_timestamp(env->sc_uptime)); -} - -static void -ramstat_close(void) -{ - log_trace(TRACE_STAT, "ramstat: close"); -} - -static void -ramstat_increment(const char *name, size_t val) -{ - struct ramstat_entry *np, lk; - - log_trace(TRACE_STAT, "ramstat: increment: %s", name); - (void)strlcpy(lk.key, name, sizeof (lk.key)); - np = RB_FIND(stats_tree, &stats, &lk); - if (np == NULL) { - np = xcalloc(1, sizeof *np); - (void)strlcpy(np->key, name, sizeof (np->key)); - RB_INSERT(stats_tree, &stats, np); - } - log_trace(TRACE_STAT, "ramstat: %s (%p): %zd -> %zd", - name, name, np->value.u.counter, np->value.u.counter + val); - np->value.u.counter += val; -} - -static void -ramstat_decrement(const char *name, size_t val) -{ - struct ramstat_entry *np, lk; - - log_trace(TRACE_STAT, "ramstat: decrement: %s", name); - (void)strlcpy(lk.key, name, sizeof (lk.key)); - np = RB_FIND(stats_tree, &stats, &lk); - if (np == NULL) { - np = xcalloc(1, sizeof *np); - (void)strlcpy(np->key, name, sizeof (np->key)); - RB_INSERT(stats_tree, &stats, np); - } - log_trace(TRACE_STAT, "ramstat: %s (%p): %zd -> %zd", - name, name, np->value.u.counter, np->value.u.counter - val); - np->value.u.counter -= val; -} - -static void -ramstat_set(const char *name, const struct stat_value *val) -{ - struct ramstat_entry *np, lk; - - log_trace(TRACE_STAT, "ramstat: set: %s", name); - (void)strlcpy(lk.key, name, sizeof (lk.key)); - np = RB_FIND(stats_tree, &stats, &lk); - if (np == NULL) { - np = xcalloc(1, sizeof *np); - (void)strlcpy(np->key, name, sizeof (np->key)); - RB_INSERT(stats_tree, &stats, np); - } - log_trace(TRACE_STAT, "ramstat: %s: n/a -> n/a", name); - np->value = *val; -} - -static int -ramstat_iter(void **iter, char **name, struct stat_value *val) -{ - struct ramstat_entry *np; - - log_trace(TRACE_STAT, "ramstat: iter"); - if (RB_EMPTY(&stats)) - return 0; - - if (*iter == NULL) - np = RB_MIN(stats_tree, &stats); - else - np = RB_NEXT(stats_tree, &stats, *iter); - - *iter = np; - if (np == NULL) - return 0; - - *name = np->key; - *val = np->value; - return 1; -} - - -static int -ramstat_entry_cmp(struct ramstat_entry *e1, struct ramstat_entry *e2) -{ - return strcmp(e1->key, e2->key); -} - -RB_GENERATE(stats_tree, ramstat_entry, entry, ramstat_entry_cmp); diff --git a/smtpd/table.5 b/smtpd/table.5 deleted file mode 100644 index e9d4fa4b..00000000 --- a/smtpd/table.5 +++ /dev/null @@ -1,258 +0,0 @@ -.\" $OpenBSD: table.5,v 1.11 2019/08/11 13:00:57 gilles Exp $ -.\" -.\" Copyright (c) 2013 Eric Faurot <eric@openbsd.org> -.\" Copyright (c) 2013 Gilles Chehade <gilles@poolp.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" -.Dd $Mdocdate: August 11 2019 $ -.Dt TABLE 5 -.Os -.Sh NAME -.Nm table -.Nd format description for smtpd tables -.Sh DESCRIPTION -This manual page documents the file format for the various tables used in the -.Xr smtpd 8 -mail daemon. -.Pp -The format described here applies to tables as defined in -.Xr smtpd.conf 5 . -.Sh TABLE TYPES -There are two types of tables: lists and mappings. -A list consists of a series of values, -while a mapping consists of a series of keys and their associated values. -The following illustrates how to declare them as static tables: -.Bd -literal -offset indent -table mylist { value1, value2, value3 } -table mymapping { key1 = value1, key2 = value2, key3 = value3 } -.Ed -.Pp -When using a -.Ql file -table, a list will be written with each value on a line by itself. -Comments can be put anywhere in the file using a hash mark -.Pq Sq # , -and extend to the end of the current line. -.Bd -literal -offset indent -value1 -value2 -value3 -.Ed -.Pp -A mapping will be written with each key and value on a line, -whitespaces separating both columns: -.Bd -literal -offset indent -key1 value1 -key2 value2 -key3 value3 -.Ed -.Pp -A file table can be converted to a Berkeley database using the -.Xr makemap 8 -utility with no syntax change. -.Pp -Tables using a -.Ql file -or Berkeley DB backend will be referenced as follows: -.Bd -unfilled -offset indent -.Ic table Ar name Cm file : Ns Pa /path/to/file -.Ic table Ar name Cm db : Ns Pa /path/to/file.db -.Ed -.Ss Aliasing tables -Aliasing tables are mappings that associate a recipient to one or many -destinations. -They can be used in two contexts: primary domain aliases and virtual domain -mapping. -.Bd -unfilled -offset indent -.Ic action Ar name method Cm alias Pf < table Ns > -.Ic action Ar name method Cm virtual Pf < table Ns > -.Ed -.Pp -In a primary domain context, the key is the user part of the recipient address, -whilst the value is one or many recipients as described in -.Xr aliases 5 : -.Bd -literal -offset indent -user1 otheruser -user2 otheruser1,otheruser2 -user3 otheruser@example.com -.Ed -.Pp -In a virtual domain context, the key is either a user part, a full email -address or a catch all, following selection rules described in -.Xr smtpd.conf 5 , -and the value is one or many recipients as described in -.Xr aliases 5 : -.Bd -literal -offset indent -user1 otheruser -user2@example.org otheruser1,otheruser2 -@example.org otheruser@example.com -@ catchall@example.com -.Ed -.Pp -The following directive shares the same table format, -but with a different meaning. -Here, the user is allowed to send mail from the listed addresses: -.Bd -unfilled -offset indent -.Ic listen on Ar interface Cm auth Oo Ar ... Oc Cm senders Pf < Ar table Ns > -.Ed -.Ss Domain tables -Domain tables are simple lists of domains or hosts. -.Bd -unfilled -offset indent -.Ic match Cm for domain Pf < table Ns > Cm action Ar name -.Ic match Cm helo Pf < table Ns > Oo Ar ... Oc Cm action Ar name -.Ed -.Pp -In that context, the list of domains will be matched against the recipient -domain or against the HELO name advertised by the sending host, -respectively. -For -.Ql static , -.Ql file -and -.Xr dbopen 3 -backends, a wildcard may be used so the domain table may contain: -.Bd -literal -offset indent -example.org -*.example.org -.Ed -.Ss Credentials tables -Credentials tables are mappings of credentials. -They can be used in two contexts: -.Bd -unfilled -offset indent -.Ic listen on Ar interface Cm tls Oo Ar ... Oc Cm auth Pf < Ar table Ns > -.Ic action Ar name Cm relay host Ar relay-url Cm auth Pf < Ar table Ns > -.Ed -.Pp -In a listener context, the credentials are a mapping of username and encrypted -passwords: -.Bd -literal -offset indent -user1 $2b$10$hIJ4QfMcp.90nJwKqGbKM.MybArjHOTpEtoTV.DgLYAiThuoYmTSe -user2 $2b$10$bwSmUOBGcZGamIfRuXGTvuTo3VLbPG9k5yeKNMBtULBhksV5KdGsK -.Ed -.Pp -The passwords are to be encrypted using the -.Xr smtpctl 8 -encrypt subcommand. -.Pp -In a relay context, the credentials are a mapping of labels and -username:password pairs: -.Bd -literal -offset indent -label1 user:password -.Ed -.Pp -The label must be unique and is used as a selector for the proper credentials -when multiple credentials are valid for a single destination. -The password is not encrypted as it must be provided to the remote host. -.Ss Netaddr tables -Netaddr tables are lists of IPv4 and IPv6 network addresses. -They can only be used in the following context: -.Pp -.D1 Ic match Cm from src Pf < Ar table Ns > Cm action Ar name -.Pp -When used as a "from source", the address of a client is compared to the list -of addresses in the table until a match is found. -.Pp -A netaddr table can contain exact addresses or netmasks, and looks as follow: -.Bd -literal -offset indent -192.168.1.1 -::1 -ipv6:::1 -192.168.1.0/24 -.Ed -.Ss Userinfo tables -User info tables are used in rule context to specify an alternate user base, -mapping virtual users to local system users by UID, GID and home directory. -.Pp -.D1 Ic action Ar name method Cm userbase Pf < Ar table Ns > -.Pp -A userinfo table looks as follows: -.Bd -literal -offset indent -joe 1000:100:/home/virtual/joe -jack 1000:100:/home/virtual/jack -.Ed -.Pp -In this example, both joe and jack are virtual users mapped to the local -system user with UID 1000 and GID 100, but different home directories. -These directories may contain a -.Xr forward 5 -file. -This can be used in conjunction with an alias table -that maps an email address or the domain part to the desired virtual -username. -For example: -.Bd -literal -offset indent -joe@example.org joe -jack@example.com jack -.Ed -.Ss Source tables -Source tables are lists of IPv4 and IPv6 addresses. -They can only be used in the following context: -.Pp -.D1 Ic action Ar name Cm relay src Pf < Ar table Ns > -.Pp -Successive queries to the source table will return the elements one by one. -.Pp -A source table looks as follow: -.Bd -literal -offset indent -192.168.1.2 -192.168.1.3 -::1 -::2 -ipv6:::3 -ipv6:::4 -.Ed -.Ss Mailaddr tables -Mailaddr tables are lists of email addresses. -They can be used in the following contexts: -.Bd -unfilled -offset indent -.Ic match Cm mail\-from Pf < Ar table Ns > Cm action Ar name -.Ic match Cm rcpt\-to Pf < Ar table Ns > Cm action Ar name -.Ed -.Pp -A mailaddr entry is used to match an email address against a username, -a domain or a full email address. -A "*" wildcard may be used in part of the domain name. -.Pp -A mailaddr table looks as follow: -.Bd -literal -offset indent -user -@domain -user@domain -user@*.domain -.Ed -.Ss Addrname tables -Addrname tables are used to map IP addresses to hostnames. -They can be used in both listen context and relay context: -.Bd -unfilled -offset indent -.Ic listen on Ar interface Cm hostnames Pf < Ar table Ns > -.Ic action Ar name Cm relay helo\-src Pf < Ar table Ns > -.Ed -.Pp -In listen context, the table is used to look up the server name to advertise -depending on the local address of the socket on which a connection is accepted. -In relay context, the table is used to determine the hostname for the HELO -sequence of the SMTP protocol, depending on the local address used for the -outgoing connection. -.Pp -The format is a mapping from inet4 or inet6 addresses to hostnames: -.Bd -literal -offset indent -::1 localhost -127.0.0.1 localhost -88.190.23.165 www.opensmtpd.org -.Ed -.Sh SEE ALSO -.Xr smtpd.conf 5 , -.Xr makemap 8 , -.Xr smtpd 8 diff --git a/smtpd/table.c b/smtpd/table.c deleted file mode 100644 index 469eeee1..00000000 --- a/smtpd/table.c +++ /dev/null @@ -1,709 +0,0 @@ -/* $OpenBSD: table.c,v 1.48 2019/01/10 07:40:52 eric Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <netinet/in.h> -#include <arpa/inet.h> -#include <net/if.h> - -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <regex.h> -#include <limits.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -struct table_backend *table_backend_lookup(const char *); - -extern struct table_backend table_backend_static; -#ifdef HAVE_DB_API -extern struct table_backend table_backend_db; -#endif -extern struct table_backend table_backend_getpwnam; -extern struct table_backend table_backend_proc; - -static const char * table_service_name(enum table_service); -static int table_parse_lookup(enum table_service, const char *, const char *, - union lookup *); -static int parse_sockaddr(struct sockaddr *, int, const char *); - -static unsigned int last_table_id = 0; - -static struct table_backend *backends[] = { - &table_backend_static, -#ifdef HAVE_DB_API - &table_backend_db, -#endif - &table_backend_getpwnam, - &table_backend_proc, - NULL -}; - -struct table_backend * -table_backend_lookup(const char *backend) -{ - int i; - - if (!strcmp(backend, "file")) - backend = "static"; - - for (i = 0; backends[i]; i++) - if (!strcmp(backends[i]->name, backend)) - return (backends[i]); - - return NULL; -} - -static const char * -table_service_name(enum table_service s) -{ - switch (s) { - case K_NONE: return "NONE"; - case K_ALIAS: return "ALIAS"; - case K_DOMAIN: return "DOMAIN"; - case K_CREDENTIALS: return "CREDENTIALS"; - case K_NETADDR: return "NETADDR"; - case K_USERINFO: return "USERINFO"; - case K_SOURCE: return "SOURCE"; - case K_MAILADDR: return "MAILADDR"; - case K_ADDRNAME: return "ADDRNAME"; - case K_MAILADDRMAP: return "MAILADDRMAP"; - case K_RELAYHOST: return "RELAYHOST"; - case K_STRING: return "STRING"; - case K_REGEX: return "REGEX"; - } - return "???"; -} - -struct table * -table_find(struct smtpd *conf, const char *name) -{ - return dict_get(conf->sc_tables_dict, name); -} - -int -table_match(struct table *table, enum table_service kind, const char *key) -{ - return table_lookup(table, kind, key, NULL); -} - -int -table_lookup(struct table *table, enum table_service kind, const char *key, - union lookup *lk) -{ - char lkey[1024], *buf = NULL; - int r; - - r = -1; - if (table->t_backend->lookup == NULL) - errno = ENOTSUP; - else if (!lowercase(lkey, key, sizeof lkey)) { - log_warnx("warn: lookup key too long: %s", key); - errno = EINVAL; - } - else - r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL); - - if (r == 1) { - log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s", - lk ? "lookup" : "match", - key, - table_service_name(kind), - table->t_backend->name, - table->t_name, - lk ? "\"" : "", - lk ? buf : "true", - lk ? "\"" : ""); - if (buf) - r = table_parse_lookup(kind, lkey, buf, lk); - } - else - log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s", - lk ? "lookup" : "match", - key, - table_service_name(kind), - table->t_backend->name, - table->t_name, - (r == -1) ? "error: " : (lk ? "none" : "false"), - (r == -1) ? strerror(errno) : ""); - - free(buf); - - return (r); -} - -int -table_fetch(struct table *table, enum table_service kind, union lookup *lk) -{ - char *buf = NULL; - int r; - - r = -1; - if (table->t_backend->fetch == NULL) - errno = ENOTSUP; - else - r = table->t_backend->fetch(table, kind, &buf); - - if (r == 1) { - log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"", - table_service_name(kind), - table->t_backend->name, - table->t_name, - buf); - r = table_parse_lookup(kind, NULL, buf, lk); - } - else - log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s", - table_service_name(kind), - table->t_backend->name, - table->t_name, - (r == -1) ? "error: " : "none", - (r == -1) ? strerror(errno) : ""); - - free(buf); - - return (r); -} - -struct table * -table_create(struct smtpd *conf, const char *backend, const char *name, - const char *config) -{ - struct table *t; - struct table_backend *tb; - char path[LINE_MAX]; - size_t n; - struct stat sb; - - if (name && table_find(conf, name)) - fatalx("table_create: table \"%s\" already defined", name); - - if ((tb = table_backend_lookup(backend)) == NULL) { - if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s", - backend) >= sizeof(path)) { - fatalx("table_create: path too long \"" - PATH_LIBEXEC"/table-%s\"", backend); - } - if (stat(path, &sb) == 0) { - tb = table_backend_lookup("proc"); - (void)strlcpy(path, backend, sizeof(path)); - if (config) { - (void)strlcat(path, ":", sizeof(path)); - if (strlcat(path, config, sizeof(path)) - >= sizeof(path)) - fatalx("table_create: config file path too long"); - } - config = path; - } - } - - if (tb == NULL) - fatalx("table_create: backend \"%s\" does not exist", backend); - - t = xcalloc(1, sizeof(*t)); - t->t_backend = tb; - - if (config) { - if (strlcpy(t->t_config, config, sizeof t->t_config) - >= sizeof t->t_config) - fatalx("table_create: table config \"%s\" too large", - t->t_config); - } - - if (strcmp(tb->name, "static") != 0) - t->t_type = T_DYNAMIC; - - if (name == NULL) - (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", - last_table_id++); - else { - n = strlcpy(t->t_name, name, sizeof(t->t_name)); - if (n >= sizeof(t->t_name)) - fatalx("table_create: table name too long"); - } - - dict_set(conf->sc_tables_dict, t->t_name, t); - - return (t); -} - -void -table_destroy(struct smtpd *conf, struct table *t) -{ - dict_xpop(conf->sc_tables_dict, t->t_name); - free(t); -} - -int -table_config(struct table *t) -{ - if (t->t_backend->config == NULL) - return (1); - return (t->t_backend->config(t)); -} - -void -table_add(struct table *t, const char *key, const char *val) -{ - if (t->t_backend->add == NULL) - fatalx("table_add: cannot add to table"); - - if (t->t_backend->add(t, key, val) == 0) - log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name); -} - -void -table_dump(struct table *t) -{ - const char *type; - char buf[LINE_MAX]; - - switch(t->t_type) { - case T_NONE: - type = "NONE"; - break; - case T_DYNAMIC: - type = "DYNAMIC"; - break; - case T_LIST: - type = "LIST"; - break; - case T_HASH: - type = "HASH"; - break; - default: - type = "???"; - break; - } - - if (t->t_config[0]) - snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config); - else - buf[0] = '\0'; - - log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name, - t->t_backend->name, type, buf); - - if (t->t_backend->dump) - t->t_backend->dump(t); -} - -int -table_check_type(struct table *t, uint32_t mask) -{ - return t->t_type & mask; -} - -int -table_check_service(struct table *t, uint32_t mask) -{ - return t->t_backend->services & mask; -} - -int -table_check_use(struct table *t, uint32_t tmask, uint32_t smask) -{ - return table_check_type(t, tmask) && table_check_service(t, smask); -} - -int -table_open(struct table *t) -{ - if (t->t_backend->open == NULL) - return (1); - return (t->t_backend->open(t)); -} - -void -table_close(struct table *t) -{ - if (t->t_backend->close) - t->t_backend->close(t); -} - -int -table_update(struct table *t) -{ - if (t->t_backend->update == NULL) - return (1); - return (t->t_backend->update(t)); -} - - -/* - * quick reminder: - * in *_match() s1 comes from session, s2 comes from table - */ - -int -table_domain_match(const char *s1, const char *s2) -{ - return hostname_match(s1, s2); -} - -int -table_mailaddr_match(const char *s1, const char *s2) -{ - struct mailaddr m1; - struct mailaddr m2; - - if (!text_to_mailaddr(&m1, s1)) - return 0; - if (!text_to_mailaddr(&m2, s2)) - return 0; - return mailaddr_match(&m1, &m2); -} - -static int table_match_mask(struct sockaddr_storage *, struct netaddr *); -static int table_inet4_match(struct sockaddr_in *, struct netaddr *); -static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); - -int -table_netaddr_match(const char *s1, const char *s2) -{ - struct netaddr n1; - struct netaddr n2; - - if (strcasecmp(s1, s2) == 0) - return 1; - if (!text_to_netaddr(&n1, s1)) - return 0; - if (!text_to_netaddr(&n2, s2)) - return 0; - if (n1.ss.ss_family != n2.ss.ss_family) - return 0; - if (SS_LEN(&n1.ss) != SS_LEN(&n2.ss)) - return 0; - return table_match_mask(&n1.ss, &n2); -} - -static int -table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) -{ - if (ss->ss_family == AF_INET) - return table_inet4_match((struct sockaddr_in *)ss, ssmask); - - if (ss->ss_family == AF_INET6) - return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); - - return (0); -} - -static int -table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) -{ - in_addr_t mask; - int i; - - /* a.b.c.d/8 -> htonl(0xff000000) */ - mask = 0; - for (i = 0; i < ssmask->bits; ++i) - mask = (mask >> 1) | 0x80000000; - mask = htonl(mask); - - /* (addr & mask) == (net & mask) */ - if ((ss->sin_addr.s_addr & mask) == - (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) - return 1; - - return 0; -} - -static int -table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) -{ - struct in6_addr *in; - struct in6_addr *inmask; - struct in6_addr mask; - int i; - - memset(&mask, 0, sizeof(mask)); - for (i = 0; i < ssmask->bits / 8; i++) - mask.s6_addr[i] = 0xff; - i = ssmask->bits % 8; - if (i) - mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i; - - in = &ss->sin6_addr; - inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; - - for (i = 0; i < 16; i++) { - if ((in->s6_addr[i] & mask.s6_addr[i]) != - (inmask->s6_addr[i] & mask.s6_addr[i])) - return (0); - } - - return (1); -} - -int -table_regex_match(const char *string, const char *pattern) -{ - regex_t preg; - int cflags = REG_EXTENDED|REG_NOSUB; - - if (strncmp(pattern, "(?i)", 4) == 0) { - cflags |= REG_ICASE; - pattern += 4; - } - - if (regcomp(&preg, pattern, cflags) != 0) - return (0); - - if (regexec(&preg, string, 0, NULL, 0) != 0) - return (0); - - return (1); -} - -void -table_dump_all(struct smtpd *conf) -{ - struct table *t; - void *iter; - - iter = NULL; - while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) - table_dump(t); -} - -void -table_open_all(struct smtpd *conf) -{ - struct table *t; - void *iter; - - iter = NULL; - while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) - if (!table_open(t)) - fatalx("failed to open table %s", t->t_name); -} - -void -table_close_all(struct smtpd *conf) -{ - struct table *t; - void *iter; - - iter = NULL; - while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) - table_close(t); -} - -static int -table_parse_lookup(enum table_service service, const char *key, - const char *line, union lookup *lk) -{ - char buffer[LINE_MAX], *p; - size_t len; - - len = strlen(line); - - switch (service) { - case K_ALIAS: - lk->expand = calloc(1, sizeof(*lk->expand)); - if (lk->expand == NULL) - return (-1); - if (!expand_line(lk->expand, line, 1)) { - expand_free(lk->expand); - return (-1); - } - return (1); - - case K_DOMAIN: - if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name)) - >= sizeof(lk->domain.name)) - return (-1); - return (1); - - case K_CREDENTIALS: - - /* credentials are stored as user:password */ - if (len < 3) - return (-1); - - /* too big to fit in a smtp session line */ - if (len >= LINE_MAX) - return (-1); - - p = strchr(line, ':'); - if (p == NULL) { - if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username)) - >= sizeof (lk->creds.username)) - return (-1); - if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password)) - >= sizeof(lk->creds.password)) - return (-1); - return (1); - } - - if (p == line || p == line + len - 1) - return (-1); - - memmove(lk->creds.username, line, p - line); - lk->creds.username[p - line] = '\0'; - - if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password)) - >= sizeof(lk->creds.password)) - return (-1); - - return (1); - - case K_NETADDR: - if (!text_to_netaddr(&lk->netaddr, line)) - return (-1); - return (1); - - case K_USERINFO: - if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line)) - return (-1); - if (!text_to_userinfo(&lk->userinfo, buffer)) - return (-1); - return (1); - - case K_SOURCE: - if (parse_sockaddr((struct sockaddr *)&lk->source.addr, - PF_UNSPEC, line) == -1) - return (-1); - return (1); - - case K_MAILADDR: - if (!text_to_mailaddr(&lk->mailaddr, line)) - return (-1); - return (1); - - case K_MAILADDRMAP: - lk->maddrmap = calloc(1, sizeof(*lk->maddrmap)); - if (lk->maddrmap == NULL) - return (-1); - maddrmap_init(lk->maddrmap); - if (!mailaddr_line(lk->maddrmap, line)) { - maddrmap_free(lk->maddrmap); - return (-1); - } - return (1); - - case K_ADDRNAME: - if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr, - PF_UNSPEC, key) == -1) - return (-1); - if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name)) - >= sizeof(lk->addrname.name)) - return (-1); - return (1); - - case K_RELAYHOST: - if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost)) - >= sizeof(lk->relayhost)) - return (-1); - return (1); - - default: - return (-1); - } -} - -static int -parse_sockaddr(struct sockaddr *sa, int family, const char *str) -{ - struct in_addr ina; - struct in6_addr in6a; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - char *cp, *str2; - const char *errstr; - - switch (family) { - case PF_UNSPEC: - if (parse_sockaddr(sa, PF_INET, str) == 0) - return (0); - return parse_sockaddr(sa, PF_INET6, str); - - case PF_INET: - if (inet_pton(PF_INET, str, &ina) != 1) - return (-1); - - sin = (struct sockaddr_in *)sa; - memset(sin, 0, sizeof *sin); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin->sin_len = sizeof(struct sockaddr_in); -#endif - sin->sin_family = PF_INET; - sin->sin_addr.s_addr = ina.s_addr; - return (0); - - case PF_INET6: - if (strncasecmp("ipv6:", str, 5) == 0) - str += 5; - cp = strchr(str, SCOPE_DELIMITER); - if (cp) { - str2 = strdup(str); - if (str2 == NULL) - return (-1); - str2[cp - str] = '\0'; - if (inet_pton(PF_INET6, str2, &in6a) != 1) { - free(str2); - return (-1); - } - cp++; - free(str2); - } else if (inet_pton(PF_INET6, str, &in6a) != 1) - return (-1); - - sin6 = (struct sockaddr_in6 *)sa; - memset(sin6, 0, sizeof *sin6); -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - sin6->sin6_family = PF_INET6; - sin6->sin6_addr = in6a; - - if (cp == NULL) - return (0); - - if (IN6_IS_ADDR_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_NODELOCAL(&in6a)) - if ((sin6->sin6_scope_id = if_nametoindex(cp))) - return (0); - - sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); - if (errstr) - return (-1); - return (0); - - default: - break; - } - - return (-1); -} diff --git a/smtpd/table_db.c b/smtpd/table_db.c deleted file mode 100644 index f7d766dd..00000000 --- a/smtpd/table_db.c +++ /dev/null @@ -1,282 +0,0 @@ -/* $OpenBSD: table_db.c,v 1.21 2019/06/28 13:32:51 deraadt Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef HAVE_DB_H -#include <db.h> -#elif defined(HAVE_DB1_DB_H) -#include <db1/db.h> -#elif defined(HAVE_DB_185_H) -#include <db_185.h> -#endif -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - - -/* db(3) backend */ -static int table_db_config(struct table *); -static int table_db_update(struct table *); -static int table_db_open(struct table *); -static void *table_db_open2(struct table *); -static int table_db_lookup(struct table *, enum table_service, const char *, char **); -static int table_db_fetch(struct table *, enum table_service, char **); -static void table_db_close(struct table *); -static void table_db_close2(void *); - -static char *table_db_get_entry(void *, const char *, size_t *); -static char *table_db_get_entry_match(void *, const char *, size_t *, - int(*)(const char *, const char *)); - -struct table_backend table_backend_db = { - "db", - K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP, - table_db_config, - NULL, - NULL, - table_db_open, - table_db_update, - table_db_close, - table_db_lookup, - table_db_fetch, -}; - -static struct keycmp { - enum table_service service; - int (*func)(const char *, const char *); -} keycmp[] = { - { K_DOMAIN, table_domain_match }, - { K_NETADDR, table_netaddr_match }, - { K_MAILADDR, table_mailaddr_match } -}; - -struct dbhandle { - DB *db; - char pathname[PATH_MAX]; - time_t mtime; - int iter; -}; - -static int -table_db_config(struct table *table) -{ - struct dbhandle *handle; - - handle = table_db_open2(table); - if (handle == NULL) - return 0; - - table_db_close2(handle); - return 1; -} - -static int -table_db_update(struct table *table) -{ - struct dbhandle *handle; - - handle = table_db_open2(table); - if (handle == NULL) - return 0; - - table_db_close2(table->t_handle); - table->t_handle = handle; - return 1; -} - -static int -table_db_open(struct table *table) -{ - table->t_handle = table_db_open2(table); - if (table->t_handle == NULL) - return 0; - return 1; -} - -static void -table_db_close(struct table *table) -{ - table_db_close2(table->t_handle); - table->t_handle = NULL; -} - -static void * -table_db_open2(struct table *table) -{ - struct dbhandle *handle; - struct stat sb; - - handle = xcalloc(1, sizeof *handle); - if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname) - >= sizeof handle->pathname) - goto error; - - if (stat(handle->pathname, &sb) == -1) - goto error; - - handle->mtime = sb.st_mtime; - handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL); - if (handle->db == NULL) - goto error; - - return handle; - -error: - if (handle->db) - handle->db->close(handle->db); - free(handle); - return NULL; -} - -static void -table_db_close2(void *hdl) -{ - struct dbhandle *handle = hdl; - handle->db->close(handle->db); - free(handle); -} - -static int -table_db_lookup(struct table *table, enum table_service service, const char *key, - char **dst) -{ - struct dbhandle *handle = table->t_handle; - char *line; - size_t len = 0; - int ret; - int (*match)(const char *, const char *) = NULL; - size_t i; - struct stat sb; - - if (stat(handle->pathname, &sb) == -1) - return -1; - - /* DB has changed, close and reopen */ - if (sb.st_mtime != handle->mtime) { - table_db_update(table); - handle = table->t_handle; - } - - for (i = 0; i < nitems(keycmp); ++i) - if (keycmp[i].service == service) - match = keycmp[i].func; - - if (match == NULL) - line = table_db_get_entry(handle, key, &len); - else - line = table_db_get_entry_match(handle, key, &len, match); - if (line == NULL) - return 0; - - ret = 1; - if (dst) - *dst = line; - else - free(line); - - return ret; -} - -static int -table_db_fetch(struct table *table, enum table_service service, char **dst) -{ - struct dbhandle *handle = table->t_handle; - DBT dbk; - DBT dbd; - int r; - - if (handle->iter == 0) - r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); - else - r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT); - handle->iter = 1; - if (!r) { - r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); - if (!r) - return 0; - } - - *dst = strdup(dbk.data); - if (*dst == NULL) - return -1; - - return 1; -} - - -static char * -table_db_get_entry_match(void *hdl, const char *key, size_t *len, - int(*func)(const char *, const char *)) -{ - struct dbhandle *handle = hdl; - DBT dbk; - DBT dbd; - int r; - char *buf = NULL; - - for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r; - r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) { - buf = xmemdup(dbk.data, dbk.size); - if (func(key, buf)) { - *len = dbk.size; - return buf; - } - free(buf); - } - return NULL; -} - -static char * -table_db_get_entry(void *hdl, const char *key, size_t *len) -{ - struct dbhandle *handle = hdl; - int ret; - DBT dbk; - DBT dbv; - char pkey[LINE_MAX]; - - /* workaround the stupidity of the DB interface */ - if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey) - errx(1, "table_db_get_entry: key too long"); - dbk.data = pkey; - dbk.size = strlen(pkey) + 1; - - if ((ret = handle->db->get(handle->db, &dbk, &dbv, 0)) != 0) - return NULL; - - *len = dbv.size; - - return xmemdup(dbv.data, dbv.size); -} diff --git a/smtpd/table_getpwnam.c b/smtpd/table_getpwnam.c deleted file mode 100644 index ccf889be..00000000 --- a/smtpd/table_getpwnam.c +++ /dev/null @@ -1,120 +0,0 @@ -/* $OpenBSD: table_getpwnam.c,v 1.12 2018/12/27 14:23:41 eric Exp $ */ - -/* - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - - -/* getpwnam(3) backend */ -static int table_getpwnam_config(struct table *); -static int table_getpwnam_update(struct table *); -static int table_getpwnam_open(struct table *); -static int table_getpwnam_lookup(struct table *, enum table_service, const char *, - char **); -static void table_getpwnam_close(struct table *); - -struct table_backend table_backend_getpwnam = { - "getpwnam", - K_USERINFO, - table_getpwnam_config, - NULL, - NULL, - table_getpwnam_open, - table_getpwnam_update, - table_getpwnam_close, - table_getpwnam_lookup, -}; - - -static int -table_getpwnam_config(struct table *table) -{ - if (table->t_config[0]) - return 0; - return 1; -} - -static int -table_getpwnam_update(struct table *table) -{ - return 1; -} - -static int -table_getpwnam_open(struct table *table) -{ - return 1; -} - -static void -table_getpwnam_close(struct table *table) -{ - return; -} - -static int -table_getpwnam_lookup(struct table *table, enum table_service kind, const char *key, - char **dst) -{ - struct passwd *pw; - - if (kind != K_USERINFO) - return -1; - - errno = 0; - do { - pw = getpwnam(key); - } while (pw == NULL && errno == EINTR); - - if (pw == NULL) { - if (errno) - return -1; - return 0; - } - if (dst == NULL) - return 1; - - if (asprintf(dst, "%d:%d:%s", - pw->pw_uid, - pw->pw_gid, - pw->pw_dir) == -1) { - *dst = NULL; - return -1; - } - - return (1); -} diff --git a/smtpd/table_proc.c b/smtpd/table_proc.c deleted file mode 100644 index 44589bd7..00000000 --- a/smtpd/table_proc.c +++ /dev/null @@ -1,283 +0,0 @@ -/* $OpenBSD: table_proc.c,v 1.16 2019/10/03 04:51:15 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#ifdef HAVE_PATHS_H -#include <paths.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -struct table_proc_priv { - pid_t pid; - struct imsgbuf ibuf; -}; - -static struct imsg imsg; -static size_t rlen; -static char *rdata; - -extern char **environ; - -static void -table_proc_call(struct table_proc_priv *p) -{ - ssize_t n; - - if (imsg_flush(&p->ibuf) == -1) { - log_warn("warn: table-proc: imsg_flush"); - fatalx("table-proc: exiting"); - } - - while (1) { - if ((n = imsg_get(&p->ibuf, &imsg)) == -1) { - log_warn("warn: table-proc: imsg_get"); - break; - } - if (n) { - rlen = imsg.hdr.len - IMSG_HEADER_SIZE; - rdata = imsg.data; - - if (imsg.hdr.type != PROC_TABLE_OK) { - log_warnx("warn: table-proc: bad response"); - break; - } - return; - } - - if ((n = imsg_read(&p->ibuf)) == -1 && errno != EAGAIN) { - log_warn("warn: table-proc: imsg_read"); - break; - } - - if (n == 0) { - log_warnx("warn: table-proc: pipe closed"); - break; - } - } - - fatalx("table-proc: exiting"); -} - -static void -table_proc_read(void *dst, size_t len) -{ - if (len > rlen) { - log_warnx("warn: table-proc: bad msg len"); - fatalx("table-proc: exiting"); - } - - if (dst) - memmove(dst, rdata, len); - - rlen -= len; - rdata += len; -} - -static void -table_proc_end(void) -{ - if (rlen) { - log_warnx("warn: table-proc: bogus data"); - fatalx("table-proc: exiting"); - } - imsg_free(&imsg); -} - -/* - * API - */ - -static int -table_proc_open(struct table *table) -{ - struct table_proc_priv *priv; - struct table_open_params op; - int fd; - - priv = xcalloc(1, sizeof(*priv)); - - fd = fork_proc_backend("table", table->t_config, table->t_name); - if (fd == -1) - fatalx("table-proc: exiting"); - - imsg_init(&priv->ibuf, fd); - - memset(&op, 0, sizeof op); - op.version = PROC_TABLE_API_VERSION; - (void)strlcpy(op.name, table->t_name, sizeof op.name); - imsg_compose(&priv->ibuf, PROC_TABLE_OPEN, 0, 0, -1, &op, sizeof op); - - table_proc_call(priv); - table_proc_end(); - - table->t_handle = priv; - - return (1); -} - -static int -table_proc_update(struct table *table) -{ - struct table_proc_priv *priv = table->t_handle; - int r; - - imsg_compose(&priv->ibuf, PROC_TABLE_UPDATE, 0, 0, -1, NULL, 0); - - table_proc_call(priv); - table_proc_read(&r, sizeof(r)); - table_proc_end(); - - return (r); -} - -static void -table_proc_close(struct table *table) -{ - struct table_proc_priv *priv = table->t_handle; - - imsg_compose(&priv->ibuf, PROC_TABLE_CLOSE, 0, 0, -1, NULL, 0); - if (imsg_flush(&priv->ibuf) == -1) - fatal("imsg_flush"); - - table->t_handle = NULL; -} - -static int -imsg_add_params(struct ibuf *buf) -{ - size_t count = 0; - - if (imsg_add(buf, &count, sizeof(count)) == -1) - return (-1); - - return (0); -} - -static int -table_proc_lookup(struct table *table, enum table_service s, const char *k, char **dst) -{ - struct table_proc_priv *priv = table->t_handle; - struct ibuf *buf; - int r; - - buf = imsg_create(&priv->ibuf, - dst ? PROC_TABLE_LOOKUP : PROC_TABLE_CHECK, 0, 0, - sizeof(s) + strlen(k) + 1); - - if (buf == NULL) - return (-1); - if (imsg_add(buf, &s, sizeof(s)) == -1) - return (-1); - if (imsg_add_params(buf) == -1) - return (-1); - if (imsg_add(buf, k, strlen(k) + 1) == -1) - return (-1); - imsg_close(&priv->ibuf, buf); - - table_proc_call(priv); - table_proc_read(&r, sizeof(r)); - - if (r == 1 && dst) { - if (rlen == 0) { - log_warnx("warn: table-proc: empty response"); - fatalx("table-proc: exiting"); - } - if (rdata[rlen - 1] != '\0') { - log_warnx("warn: table-proc: not NUL-terminated"); - fatalx("table-proc: exiting"); - } - *dst = strdup(rdata); - if (*dst == NULL) - r = -1; - table_proc_read(NULL, rlen); - } - - table_proc_end(); - - return (r); -} - -static int -table_proc_fetch(struct table *table, enum table_service s, char **dst) -{ - struct table_proc_priv *priv = table->t_handle; - struct ibuf *buf; - int r; - - buf = imsg_create(&priv->ibuf, PROC_TABLE_FETCH, 0, 0, sizeof(s)); - if (buf == NULL) - return (-1); - if (imsg_add(buf, &s, sizeof(s)) == -1) - return (-1); - if (imsg_add_params(buf) == -1) - return (-1); - imsg_close(&priv->ibuf, buf); - - table_proc_call(priv); - table_proc_read(&r, sizeof(r)); - - if (r == 1) { - if (rlen == 0) { - log_warnx("warn: table-proc: empty response"); - fatalx("table-proc: exiting"); - } - if (rdata[rlen - 1] != '\0') { - log_warnx("warn: table-proc: not NUL-terminated"); - fatalx("table-proc: exiting"); - } - *dst = strdup(rdata); - if (*dst == NULL) - r = -1; - table_proc_read(NULL, rlen); - } - - table_proc_end(); - - return (r); -} - -struct table_backend table_backend_proc = { - "proc", - K_ANY, - NULL, - NULL, - NULL, - table_proc_open, - table_proc_update, - table_proc_close, - table_proc_lookup, - table_proc_fetch, -}; diff --git a/smtpd/table_static.c b/smtpd/table_static.c deleted file mode 100644 index 8f78ae11..00000000 --- a/smtpd/table_static.c +++ /dev/null @@ -1,398 +0,0 @@ -/* $OpenBSD: table_static.c,v 1.32 2018/12/28 14:21:02 eric Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <errno.h> - -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - -struct table_static_priv { - int type; - struct dict dict; - void *iter; -}; - -/* static backend */ -static int table_static_config(struct table *); -static int table_static_add(struct table *, const char *, const char *); -static void table_static_dump(struct table *); -static int table_static_update(struct table *); -static int table_static_open(struct table *); -static int table_static_lookup(struct table *, enum table_service, const char *, - char **); -static int table_static_fetch(struct table *, enum table_service, char **); -static void table_static_close(struct table *); - -struct table_backend table_backend_static = { - "static", - K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO| - K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST| - K_STRING|K_REGEX, - table_static_config, - table_static_add, - table_static_dump, - table_static_open, - table_static_update, - table_static_close, - table_static_lookup, - table_static_fetch -}; - -static struct keycmp { - enum table_service service; - int (*func)(const char *, const char *); -} keycmp[] = { - { K_DOMAIN, table_domain_match }, - { K_NETADDR, table_netaddr_match }, - { K_MAILADDR, table_mailaddr_match }, - { K_REGEX, table_regex_match }, -}; - - -static void -table_static_priv_free(struct table_static_priv *priv) -{ - void *p; - - while (dict_poproot(&priv->dict, (void **)&p)) - if (p != priv) - free(p); - free(priv); -} - -static int -table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val) -{ - char lkey[1024]; - void *old, *new = NULL; - - if (!lowercase(lkey, key, sizeof lkey)) { - errno = ENAMETOOLONG; - return (-1); - } - - if (val) { - new = strdup(val); - if (new == NULL) - return (-1); - } - - /* use priv if value is null, so we can detect duplicate entries */ - old = dict_set(&priv->dict, lkey, new ? new : priv); - if (old) { - if (old != priv) - free(old); - return (1); - } - - return (0); -} - -static int -table_static_priv_load(struct table_static_priv *priv, const char *path) -{ - FILE *fp; - char *buf = NULL, *p; - int lineno = 0; - size_t sz = 0; - ssize_t flen; - char *keyp; - char *valp; - int ret = 0; - - if ((fp = fopen(path, "r")) == NULL) { - log_warn("%s: fopen", path); - return 0; - } - - while ((flen = getline(&buf, &sz, fp)) != -1) { - lineno++; - if (buf[flen - 1] == '\n') - buf[--flen] = '\0'; - - keyp = buf; - while (isspace((unsigned char)*keyp)) { - ++keyp; - --flen; - } - if (*keyp == '\0') - continue; - while (isspace((unsigned char)keyp[flen - 1])) - keyp[--flen] = '\0'; - if (*keyp == '#') { - if (priv->type == T_NONE) { - keyp++; - while (isspace((unsigned char)*keyp)) - ++keyp; - if (!strcmp(keyp, "@list")) - priv->type = T_LIST; - } - continue; - } - - if (priv->type == T_NONE) { - for (p = keyp; *p; p++) { - if (*p == ' ' || *p == '\t' || *p == ':') { - priv->type = T_HASH; - break; - } - } - if (priv->type == T_NONE) - priv->type = T_LIST; - } - - if (priv->type == T_LIST) { - table_static_priv_add(priv, keyp, NULL); - continue; - } - - /* T_HASH */ - valp = keyp; - strsep(&valp, " \t:"); - if (valp) { - while (*valp) { - if (!isspace((unsigned char)*valp) && - !(*valp == ':' && - isspace((unsigned char)*(valp + 1)))) - break; - ++valp; - } - if (*valp == '\0') - valp = NULL; - } - if (valp == NULL) { - log_warnx("%s: invalid map entry line %d", - path, lineno); - goto end; - } - - table_static_priv_add(priv, keyp, valp); - } - - if (ferror(fp)) { - log_warn("%s: getline", path); - goto end; - } - - /* Accept empty alias files; treat them as hashes */ - if (priv->type == T_NONE) - priv->type = T_HASH; - - ret = 1; -end: - free(buf); - fclose(fp); - return ret; -} - -static int -table_static_config(struct table *t) -{ - struct table_static_priv *priv, *old; - - /* already up, and no config file? ok */ - if (t->t_handle && *t->t_config == '\0') - return 1; - - /* new config */ - priv = calloc(1, sizeof(*priv)); - if (priv == NULL) - return 0; - priv->type = t->t_type; - dict_init(&priv->dict); - - if (*t->t_config) { - /* load the config file */ - if (table_static_priv_load(priv, t->t_config) == 0) { - table_static_priv_free(priv); - return 0; - } - } - - if ((old = t->t_handle)) - table_static_priv_free(old); - t->t_handle = priv; - t->t_type = priv->type; - - return 1; -} - -static int -table_static_add(struct table *table, const char *key, const char *val) -{ - struct table_static_priv *priv = table->t_handle; - int r; - - /* cannot add to a table read from a file */ - if (*table->t_config) - return 0; - - if (table->t_type == T_NONE) - table->t_type = val ? T_HASH : T_LIST; - else if (table->t_type == T_LIST && val) - return 0; - else if (table->t_type == T_HASH && val == NULL) - return 0; - - if (priv == NULL) { - if (table_static_config(table) == 0) - return 0; - priv = table->t_handle; - } - - r = table_static_priv_add(priv, key, val); - if (r == -1) - return 0; - return 1; -} - -static void -table_static_dump(struct table *table) -{ - struct table_static_priv *priv = table->t_handle; - const char *key; - char *value; - void *iter; - - iter = NULL; - while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) { - if (value && (void*)value != (void*)priv) - log_debug(" \"%s\" -> \"%s\"", key, value); - else - log_debug(" \"%s\"", key); - } -} - -static int -table_static_update(struct table *table) -{ - if (table_static_config(table) == 1) { - log_info("info: Table \"%s\" successfully updated", table->t_name); - return 1; - } - - log_info("info: Failed to update table \"%s\"", table->t_name); - return 0; -} - -static int -table_static_open(struct table *table) -{ - if (table->t_handle == NULL) - return table_static_config(table); - return 1; -} - -static void -table_static_close(struct table *table) -{ - struct table_static_priv *priv = table->t_handle; - - if (priv) - table_static_priv_free(priv); - table->t_handle = NULL; -} - -static int -table_static_lookup(struct table *table, enum table_service service, const char *key, - char **dst) -{ - struct table_static_priv *priv = table->t_handle; - char *line; - int ret; - int (*match)(const char *, const char *) = NULL; - size_t i; - void *iter; - const char *k; - char *v; - - for (i = 0; i < nitems(keycmp); ++i) - if (keycmp[i].service == service) - match = keycmp[i].func; - - line = NULL; - iter = NULL; - ret = 0; - while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) { - if (match) { - if (match(key, k)) { - line = v; - ret = 1; - } - } - else { - if (strcmp(key, k) == 0) { - line = v; - ret = 1; - } - } - if (ret) - break; - } - - if (dst == NULL) - return ret ? 1 : 0; - - if (ret == 0) - return 0; - - *dst = strdup(line); - if (*dst == NULL) - return -1; - - return 1; -} - -static int -table_static_fetch(struct table *t, enum table_service service, char **dst) -{ - struct table_static_priv *priv = t->t_handle; - const char *k; - - if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) { - priv->iter = NULL; - if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) - return 0; - } - - *dst = strdup(k); - if (*dst == NULL) - return -1; - - return 1; -} diff --git a/smtpd/to.c b/smtpd/to.c deleted file mode 100644 index 81a1bb54..00000000 --- a/smtpd/to.c +++ /dev/null @@ -1,880 +0,0 @@ -/* $OpenBSD: to.c,v 1.44 2019/11/12 20:21:46 gilles Exp $ */ - -/* - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <limits.h> -#include <inttypes.h> -#include <netdb.h> -#include <pwd.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -static const char *in6addr_to_text(const struct in6_addr *); -static int alias_is_filter(struct expandnode *, const char *, size_t); -static int alias_is_username(struct expandnode *, const char *, size_t); -static int alias_is_address(struct expandnode *, const char *, size_t); -static int alias_is_filename(struct expandnode *, const char *, size_t); -static int alias_is_include(struct expandnode *, const char *, size_t); -static int alias_is_error(struct expandnode *, const char *, size_t); - -static int broken_inet_net_pton_ipv6(const char *, void *, size_t); - -const char * -sockaddr_to_text(struct sockaddr *sa) -{ - static char buf[NI_MAXHOST]; - - if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0, - NI_NUMERICHOST)) - return ("(unknown)"); - else - return (buf); -} - -static const char * -in6addr_to_text(const struct in6_addr *addr) -{ - struct sockaddr_in6 sa_in6; - uint16_t tmp16; - - memset(&sa_in6, 0, sizeof(sa_in6)); -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sa_in6.sin6_len = sizeof(sa_in6); -#endif - sa_in6.sin6_family = AF_INET6; - memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); - - /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ - if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || - IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { - memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); - sa_in6.sin6_scope_id = ntohs(tmp16); - sa_in6.sin6_addr.s6_addr[2] = 0; - sa_in6.sin6_addr.s6_addr[3] = 0; - } - - return (sockaddr_to_text((struct sockaddr *)&sa_in6)); -} - -int -text_to_mailaddr(struct mailaddr *maddr, const char *email) -{ - char *username; - char *hostname; - char buffer[LINE_MAX]; - - if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer) - return 0; - - memset(maddr, 0, sizeof *maddr); - - username = buffer; - hostname = strrchr(username, '@'); - - if (hostname == NULL) { - if (strlcpy(maddr->user, username, sizeof maddr->user) - >= sizeof maddr->user) - return 0; - } - else if (username == hostname) { - *hostname++ = '\0'; - if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) - >= sizeof maddr->domain) - return 0; - } - else { - *hostname++ = '\0'; - if (strlcpy(maddr->user, username, sizeof maddr->user) - >= sizeof maddr->user) - return 0; - if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) - >= sizeof maddr->domain) - return 0; - } - - return 1; -} - -const char * -mailaddr_to_text(const struct mailaddr *maddr) -{ - static char buffer[LINE_MAX]; - - (void)strlcpy(buffer, maddr->user, sizeof buffer); - (void)strlcat(buffer, "@", sizeof buffer); - if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer) - return NULL; - - return buffer; -} - - -const char * -sa_to_text(const struct sockaddr *sa) -{ - static char buf[NI_MAXHOST + 5]; - char *p; - - buf[0] = '\0'; - p = buf; - - if (sa->sa_family == AF_LOCAL) - (void)strlcpy(buf, "local", sizeof buf); - else if (sa->sa_family == AF_INET) { - in_addr_t addr; - - addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr; - addr = ntohl(addr); - (void)bsnprintf(p, NI_MAXHOST, "%d.%d.%d.%d", - (addr >> 24) & 0xff, (addr >> 16) & 0xff, - (addr >> 8) & 0xff, addr & 0xff); - } - else if (sa->sa_family == AF_INET6) { - const struct sockaddr_in6 *in6; - const struct in6_addr *in6_addr; - - in6 = (const struct sockaddr_in6 *)sa; - p = buf; - in6_addr = &in6->sin6_addr; - (void)bsnprintf(p, NI_MAXHOST, "[%s]", in6addr_to_text(in6_addr)); - } - - return (buf); -} - -const char * -ss_to_text(const struct sockaddr_storage *ss) -{ - return (sa_to_text((const struct sockaddr*)ss)); -} - -const char * -time_to_text(time_t when) -{ - struct tm *lt; - static char buf[40]; - char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", - "Jul","Aug","Sep","Oct","Nov","Dec"}; - const char *tz; - long offset; - - lt = localtime(&when); - if (lt == NULL || when == 0) - fatalx("time_to_text: localtime"); - -#if HAVE_STRUCT_TM_TM_GMTOFF - offset = lt->tm_gmtoff; - tz = lt->tm_zone; -#elif defined HAVE_DECL_ALTZONE && defined HAVE_DECL_TIMEZONE - offset = lt->tm_isdst > 0 ? altzone : timezone; - tz = lt->tm_isdst > 0 ? tzname[1] : tzname[0]; -#endif - - /* We do not use strftime because it is subject to locale substitution*/ - if (!bsnprintf(buf, sizeof(buf), - "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)", - day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon], - lt->tm_year + 1900, - lt->tm_hour, lt->tm_min, lt->tm_sec, - offset >= 0 ? '+' : '-', - abs((int)offset / 3600), - abs((int)offset % 3600) / 60, - tz)) - fatalx("time_to_text: bsnprintf"); - - return buf; -} - -const char * -duration_to_text(time_t t) -{ - static char dst[64]; - char buf[64]; - int h, m, s; - long long d; - - if (t == 0) { - (void)strlcpy(dst, "0s", sizeof dst); - return (dst); - } - - dst[0] = '\0'; - if (t < 0) { - (void)strlcpy(dst, "-", sizeof dst); - t = -t; - } - - s = t % 60; - t /= 60; - m = t % 60; - t /= 60; - h = t % 24; - d = t / 24; - - if (d) { - (void)snprintf(buf, sizeof buf, "%lldd", d); - (void)strlcat(dst, buf, sizeof dst); - } - if (h) { - (void)snprintf(buf, sizeof buf, "%dh", h); - (void)strlcat(dst, buf, sizeof dst); - } - if (m) { - (void)snprintf(buf, sizeof buf, "%dm", m); - (void)strlcat(dst, buf, sizeof dst); - } - if (s) { - (void)snprintf(buf, sizeof buf, "%ds", s); - (void)strlcat(dst, buf, sizeof dst); - } - - return (dst); -} - -int -text_to_netaddr(struct netaddr *netaddr, const char *s) -{ - struct sockaddr_storage ss; - struct sockaddr_in ssin; - struct sockaddr_in6 ssin6; - int bits; - char buf[NI_MAXHOST]; - size_t len; - - memset(&ssin, 0, sizeof(struct sockaddr_in)); - memset(&ssin6, 0, sizeof(struct sockaddr_in6)); - - if (strncasecmp("IPv6:", s, 5) == 0) - s += 5; - - bits = inet_net_pton(AF_INET, s, &ssin.sin_addr, - sizeof(struct in_addr)); - if (bits != -1) { - ssin.sin_family = AF_INET; - memcpy(&ss, &ssin, sizeof(ssin)); -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN - ss.ss_len = sizeof(struct sockaddr_in); -#endif - } else { - if (s[0] != '[') { - if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) - return 0; - } - else { - s++; - if (strncasecmp("IPv6:", s, 5) == 0) - s += 5; - if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) - return 0; - if (buf[len-1] != ']') - return 0; - buf[len-1] = 0; - } - bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr, - sizeof(struct in6_addr)); - if (bits == -1) { - if (errno != EAFNOSUPPORT) - return 0; - bits = broken_inet_net_pton_ipv6(buf, &ssin6.sin6_addr, - sizeof(struct in6_addr)); - if (bits == -1) - return 0; - } - ssin6.sin6_family = AF_INET6; - memcpy(&ss, &ssin6, sizeof(ssin6)); -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN - ss.ss_len = sizeof(struct sockaddr_in6); -#endif - } - - netaddr->ss = ss; - netaddr->bits = bits; - return 1; -} - -int -text_to_relayhost(struct relayhost *relay, const char *s) -{ - static const struct schema { - const char *name; - int tls; - uint16_t flags; - uint16_t port; - } schemas [] = { - /* - * new schemas should be *appended* otherwise the default - * schema index needs to be updated later in this function. - */ - { "smtp://", RELAY_TLS_OPPORTUNISTIC, 0, 25 }, - { "smtp+tls://", RELAY_TLS_STARTTLS, 0, 25 }, - { "smtp+notls://", RELAY_TLS_NO, 0, 25 }, - /* need to specify an explicit port for LMTP */ - { "lmtp://", RELAY_TLS_NO, RELAY_LMTP, 0 }, - { "smtps://", RELAY_TLS_SMTPS, 0, 465 } - }; - const char *errstr = NULL; - char *p, *q; - char buffer[1024]; - char *beg, *end; - size_t i; - size_t len; - - memset(buffer, 0, sizeof buffer); - if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) - return 0; - - for (i = 0; i < nitems(schemas); ++i) - if (strncasecmp(schemas[i].name, s, - strlen(schemas[i].name)) == 0) - break; - - if (i == nitems(schemas)) { - /* there is a schema, but it's not recognized */ - if (strstr(buffer, "://")) - return 0; - - /* no schema, default to smtp:// */ - i = 0; - p = buffer; - } - else - p = buffer + strlen(schemas[i].name); - - relay->tls = schemas[i].tls; - relay->flags = schemas[i].flags; - relay->port = schemas[i].port; - - /* first, we extract the label if any */ - if ((q = strchr(p, '@')) != NULL) { - *q = 0; - if (strlcpy(relay->authlabel, p, sizeof (relay->authlabel)) - >= sizeof (relay->authlabel)) - return 0; - p = q + 1; - } - - /* then, we extract the mail exchanger */ - beg = end = p; - if (*beg == '[') { - if ((end = strchr(beg, ']')) == NULL) - return 0; - /* skip ']', it has to be included in the relay hostname */ - ++end; - len = end - beg; - } - else { - for (end = beg; *end; ++end) - if (!isalnum((unsigned char)*end) && - *end != '_' && *end != '.' && *end != '-') - break; - len = end - beg; - } - if (len >= sizeof relay->hostname) - return 0; - for (i = 0; i < len; ++i) - relay->hostname[i] = beg[i]; - relay->hostname[i] = 0; - - /* finally, we extract the port */ - p = beg + len; - if (*p == ':') { - relay->port = strtonum(p+1, 1, IPPORT_HILASTAUTO, &errstr); - if (errstr) - return 0; - } - - if (!valid_domainpart(relay->hostname)) - return 0; - if ((relay->flags & RELAY_LMTP) && (relay->port == 0)) - return 0; - if (relay->authlabel[0]) { - /* disallow auth on non-tls scheme. */ - if (relay->tls != RELAY_TLS_STARTTLS && - relay->tls != RELAY_TLS_SMTPS) - return 0; - relay->flags |= RELAY_AUTH; - } - - return 1; -} - -uint64_t -text_to_evpid(const char *s) -{ - uint64_t ulval; - char *ep; - - errno = 0; - ulval = strtoull(s, &ep, 16); - if (s[0] == '\0' || *ep != '\0') - return 0; - if (errno == ERANGE && ulval == ULLONG_MAX) - return 0; - if (ulval == 0) - return 0; - return (ulval); -} - -uint32_t -text_to_msgid(const char *s) -{ - uint64_t ulval; - char *ep; - - errno = 0; - ulval = strtoull(s, &ep, 16); - if (s[0] == '\0' || *ep != '\0') - return 0; - if (errno == ERANGE && ulval == ULLONG_MAX) - return 0; - if (ulval == 0) - return 0; - if (ulval > 0xffffffff) - return 0; - return (ulval & 0xffffffff); -} - -const char * -rule_to_text(struct rule *r) -{ - static char buf[4096]; - - memset(buf, 0, sizeof buf); - (void)strlcpy(buf, "match", sizeof buf); - if (r->flag_tag) { - if (r->flag_tag < 0) - (void)strlcat(buf, " !", sizeof buf); - (void)strlcat(buf, " tag ", sizeof buf); - (void)strlcat(buf, r->table_tag, sizeof buf); - } - - if (r->flag_from) { - if (r->flag_from < 0) - (void)strlcat(buf, " !", sizeof buf); - if (r->flag_from_socket) - (void)strlcat(buf, " from socket", sizeof buf); - else if (r->flag_from_rdns) { - (void)strlcat(buf, " from rdns", sizeof buf); - if (r->table_from) { - (void)strlcat(buf, " ", sizeof buf); - (void)strlcat(buf, r->table_from, sizeof buf); - } - } - else if (strcmp(r->table_from, "<anyhost>") == 0) - (void)strlcat(buf, " from any", sizeof buf); - else if (strcmp(r->table_from, "<localhost>") == 0) - (void)strlcat(buf, " from local", sizeof buf); - else { - (void)strlcat(buf, " from src ", sizeof buf); - (void)strlcat(buf, r->table_from, sizeof buf); - } - } - - if (r->flag_for) { - if (r->flag_for < 0) - (void)strlcat(buf, " !", sizeof buf); - if (strcmp(r->table_for, "<anydestination>") == 0) - (void)strlcat(buf, " for any", sizeof buf); - else if (strcmp(r->table_for, "<localnames>") == 0) - (void)strlcat(buf, " for local", sizeof buf); - else { - (void)strlcat(buf, " for domain ", sizeof buf); - (void)strlcat(buf, r->table_for, sizeof buf); - } - } - - if (r->flag_smtp_helo) { - if (r->flag_smtp_helo < 0) - (void)strlcat(buf, " !", sizeof buf); - (void)strlcat(buf, " helo ", sizeof buf); - (void)strlcat(buf, r->table_smtp_helo, sizeof buf); - } - - if (r->flag_smtp_auth) { - if (r->flag_smtp_auth < 0) - (void)strlcat(buf, " !", sizeof buf); - (void)strlcat(buf, " auth", sizeof buf); - if (r->table_smtp_auth) { - (void)strlcat(buf, " ", sizeof buf); - (void)strlcat(buf, r->table_smtp_auth, sizeof buf); - } - } - - if (r->flag_smtp_starttls) { - if (r->flag_smtp_starttls < 0) - (void)strlcat(buf, " !", sizeof buf); - (void)strlcat(buf, " tls", sizeof buf); - } - - if (r->flag_smtp_mail_from) { - if (r->flag_smtp_mail_from < 0) - (void)strlcat(buf, " !", sizeof buf); - (void)strlcat(buf, " mail-from ", sizeof buf); - (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf); - } - - if (r->flag_smtp_rcpt_to) { - if (r->flag_smtp_rcpt_to < 0) - (void)strlcat(buf, " !", sizeof buf); - (void)strlcat(buf, " rcpt-to ", sizeof buf); - (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf); - } - (void)strlcat(buf, " action ", sizeof buf); - if (r->reject) - (void)strlcat(buf, "reject", sizeof buf); - else - (void)strlcat(buf, r->dispatcher, sizeof buf); - return buf; -} - - -int -text_to_userinfo(struct userinfo *userinfo, const char *s) -{ - char buf[PATH_MAX]; - char *p; - const char *errstr; - - memset(buf, 0, sizeof buf); - p = buf; - while (*s && *s != ':') - *p++ = *s++; - if (*s++ != ':') - goto error; - - if (strlcpy(userinfo->username, buf, - sizeof userinfo->username) >= sizeof userinfo->username) - goto error; - - memset(buf, 0, sizeof buf); - p = buf; - while (*s && *s != ':') - *p++ = *s++; - if (*s++ != ':') - goto error; - userinfo->uid = strtonum(buf, 0, UID_MAX, &errstr); - if (errstr) - goto error; - - memset(buf, 0, sizeof buf); - p = buf; - while (*s && *s != ':') - *p++ = *s++; - if (*s++ != ':') - goto error; - userinfo->gid = strtonum(buf, 0, GID_MAX, &errstr); - if (errstr) - goto error; - - if (strlcpy(userinfo->directory, s, - sizeof userinfo->directory) >= sizeof userinfo->directory) - goto error; - - return 1; - -error: - return 0; -} - -int -text_to_credentials(struct credentials *creds, const char *s) -{ - char *p; - char buffer[LINE_MAX]; - size_t offset; - - p = strchr(s, ':'); - if (p == NULL) { - creds->username[0] = '\0'; - if (strlcpy(creds->password, s, sizeof creds->password) - >= sizeof creds->password) - return 0; - return 1; - } - - offset = p - s; - - memset(buffer, 0, sizeof buffer); - if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) - return 0; - p = buffer + offset; - *p = '\0'; - - if (strlcpy(creds->username, buffer, sizeof creds->username) - >= sizeof creds->username) - return 0; - if (strlcpy(creds->password, p+1, sizeof creds->password) - >= sizeof creds->password) - return 0; - - return 1; -} - -int -text_to_expandnode(struct expandnode *expandnode, const char *s) -{ - size_t l; - - l = strlen(s); - if (alias_is_error(expandnode, s, l) || - alias_is_include(expandnode, s, l) || - alias_is_filter(expandnode, s, l) || - alias_is_filename(expandnode, s, l) || - alias_is_address(expandnode, s, l) || - alias_is_username(expandnode, s, l)) - return (1); - - return (0); -} - -const char * -expandnode_to_text(struct expandnode *expandnode) -{ - switch (expandnode->type) { - case EXPAND_FILTER: - case EXPAND_FILENAME: - case EXPAND_INCLUDE: - case EXPAND_ERROR: - case EXPAND_USERNAME: - return expandnode->u.user; - case EXPAND_ADDRESS: - return mailaddr_to_text(&expandnode->u.mailaddr); - case EXPAND_INVALID: - break; - } - - return NULL; -} - -/******/ -static int -alias_is_filter(struct expandnode *alias, const char *line, size_t len) -{ - int v = 0; - - if (*line == '"') - v = 1; - if (*(line+v) == '|') { - if (strlcpy(alias->u.buffer, line + v + 1, - sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) - return 0; - if (v) { - v = strlen(alias->u.buffer); - if (v == 0) - return (0); - if (alias->u.buffer[v-1] != '"') - return (0); - alias->u.buffer[v-1] = '\0'; - } - alias->type = EXPAND_FILTER; - return (1); - } - return (0); -} - -static int -alias_is_username(struct expandnode *alias, const char *line, size_t len) -{ - memset(alias, 0, sizeof *alias); - - if (strlcpy(alias->u.user, line, - sizeof(alias->u.user)) >= sizeof(alias->u.user)) - return 0; - - while (*line) { - if (!isalnum((unsigned char)*line) && - *line != '_' && *line != '.' && *line != '-' && *line != '+') - return 0; - ++line; - } - - alias->type = EXPAND_USERNAME; - return 1; -} - -static int -alias_is_address(struct expandnode *alias, const char *line, size_t len) -{ - char *domain; - - memset(alias, 0, sizeof *alias); - - if (len < 3) /* x@y */ - return 0; - - domain = strchr(line, '@'); - if (domain == NULL) - return 0; - - /* @ cannot start or end an address */ - if (domain == line || domain == line + len - 1) - return 0; - - /* scan pre @ for disallowed chars */ - *domain++ = '\0'; - (void)strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user)); - (void)strlcpy(alias->u.mailaddr.domain, domain, - sizeof(alias->u.mailaddr.domain)); - - while (*line) { - char allowedset[] = "!#$%*/?|^{}`~&'+-=_."; - if (!isalnum((unsigned char)*line) && - strchr(allowedset, *line) == NULL) - return 0; - ++line; - } - - while (*domain) { - char allowedset[] = "-."; - if (!isalnum((unsigned char)*domain) && - strchr(allowedset, *domain) == NULL) - return 0; - ++domain; - } - - alias->type = EXPAND_ADDRESS; - return 1; -} - -static int -alias_is_filename(struct expandnode *alias, const char *line, size_t len) -{ - memset(alias, 0, sizeof *alias); - - if (*line != '/') - return 0; - - if (strlcpy(alias->u.buffer, line, - sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) - return 0; - alias->type = EXPAND_FILENAME; - return 1; -} - -static int -alias_is_include(struct expandnode *alias, const char *line, size_t len) -{ - size_t skip; - - memset(alias, 0, sizeof *alias); - - if (strncasecmp(":include:", line, 9) == 0) - skip = 9; - else if (strncasecmp("include:", line, 8) == 0) - skip = 8; - else - return 0; - - if (!alias_is_filename(alias, line + skip, len - skip)) - return 0; - - alias->type = EXPAND_INCLUDE; - return 1; -} - -static int -alias_is_error(struct expandnode *alias, const char *line, size_t len) -{ - size_t skip; - - memset(alias, 0, sizeof *alias); - - if (strncasecmp(":error:", line, 7) == 0) - skip = 7; - else if (strncasecmp("error:", line, 6) == 0) - skip = 6; - else - return 0; - - if (strlcpy(alias->u.buffer, line + skip, - sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) - return 0; - - if (strlen(alias->u.buffer) < 5) - return 0; - - /* [45][0-9]{2} [a-zA-Z0-9].* */ - if (alias->u.buffer[3] != ' ' || - !isalnum((unsigned char)alias->u.buffer[4]) || - (alias->u.buffer[0] != '4' && alias->u.buffer[0] != '5') || - !isdigit((unsigned char)alias->u.buffer[1]) || - !isdigit((unsigned char)alias->u.buffer[2])) - return 0; - - alias->type = EXPAND_ERROR; - return 1; -} - -static int -broken_inet_net_pton_ipv6(const char *src, void *dst, size_t size) -{ - int ret; - int bits; - char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")]; - char *sep; - const char *errstr; - - if (strlcpy(buf, src, sizeof buf) >= sizeof buf) { - errno = EMSGSIZE; - return (-1); - } - - sep = strchr(buf, '/'); - if (sep != NULL) - *sep++ = '\0'; - - ret = inet_pton(AF_INET6, buf, dst); - if (ret != 1) - return (-1); - - if (sep == NULL) - return 128; - - bits = strtonum(sep, 0, 128, &errstr); - if (errstr) - return (-1); - - return bits; -} diff --git a/smtpd/tree.c b/smtpd/tree.c deleted file mode 100644 index 1d720a59..00000000 --- a/smtpd/tree.c +++ /dev/null @@ -1,259 +0,0 @@ -/* $OpenBSD: tree.c,v 1.6 2018/12/23 16:06:24 gilles Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/tree.h> - -#include <err.h> -#include <inttypes.h> -#include <stdlib.h> -#include <limits.h> - -#include "tree.h" - -struct treeentry { - SPLAY_ENTRY(treeentry) entry; - uint64_t id; - void *data; -}; - -static int treeentry_cmp(struct treeentry *, struct treeentry *); - -SPLAY_PROTOTYPE(_tree, treeentry, entry, treeentry_cmp); - -int -tree_check(struct tree *t, uint64_t id) -{ - struct treeentry key; - - key.id = id; - return (SPLAY_FIND(_tree, &t->tree, &key) != NULL); -} - -void * -tree_set(struct tree *t, uint64_t id, void *data) -{ - struct treeentry *entry, key; - char *old; - - key.id = id; - if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) { - if ((entry = malloc(sizeof *entry)) == NULL) - err(1, "tree_set: malloc"); - entry->id = id; - SPLAY_INSERT(_tree, &t->tree, entry); - old = NULL; - t->count += 1; - } else - old = entry->data; - - entry->data = data; - - return (old); -} - -void -tree_xset(struct tree *t, uint64_t id, void *data) -{ - struct treeentry *entry; - - if ((entry = malloc(sizeof *entry)) == NULL) - err(1, "tree_xset: malloc"); - entry->id = id; - entry->data = data; - if (SPLAY_INSERT(_tree, &t->tree, entry)) - errx(1, "tree_xset(%p, 0x%016"PRIx64 ")", t, id); - t->count += 1; -} - -void * -tree_get(struct tree *t, uint64_t id) -{ - struct treeentry key, *entry; - - key.id = id; - if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) - return (NULL); - - return (entry->data); -} - -void * -tree_xget(struct tree *t, uint64_t id) -{ - struct treeentry key, *entry; - - key.id = id; - if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) - errx(1, "tree_get(%p, 0x%016"PRIx64 ")", t, id); - - return (entry->data); -} - -void * -tree_pop(struct tree *t, uint64_t id) -{ - struct treeentry key, *entry; - void *data; - - key.id = id; - if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) - return (NULL); - - data = entry->data; - SPLAY_REMOVE(_tree, &t->tree, entry); - free(entry); - t->count -= 1; - - return (data); -} - -void * -tree_xpop(struct tree *t, uint64_t id) -{ - struct treeentry key, *entry; - void *data; - - key.id = id; - if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) - errx(1, "tree_xpop(%p, 0x%016" PRIx64 ")", t, id); - - data = entry->data; - SPLAY_REMOVE(_tree, &t->tree, entry); - free(entry); - t->count -= 1; - - return (data); -} - -int -tree_poproot(struct tree *t, uint64_t *id, void **data) -{ - struct treeentry *entry; - - entry = SPLAY_ROOT(&t->tree); - if (entry == NULL) - return (0); - if (id) - *id = entry->id; - if (data) - *data = entry->data; - SPLAY_REMOVE(_tree, &t->tree, entry); - free(entry); - t->count -= 1; - - return (1); -} - -int -tree_root(struct tree *t, uint64_t *id, void **data) -{ - struct treeentry *entry; - - entry = SPLAY_ROOT(&t->tree); - if (entry == NULL) - return (0); - if (id) - *id = entry->id; - if (data) - *data = entry->data; - return (1); -} - -int -tree_iter(struct tree *t, void **hdl, uint64_t *id, void **data) -{ - struct treeentry *curr = *hdl; - - if (curr == NULL) - curr = SPLAY_MIN(_tree, &t->tree); - else - curr = SPLAY_NEXT(_tree, &t->tree, curr); - - if (curr) { - *hdl = curr; - if (id) - *id = curr->id; - if (data) - *data = curr->data; - return (1); - } - - return (0); -} - -int -tree_iterfrom(struct tree *t, void **hdl, uint64_t k, uint64_t *id, void **data) -{ - struct treeentry *curr = *hdl, key; - - if (curr == NULL) { - if (k == 0) - curr = SPLAY_MIN(_tree, &t->tree); - else { - key.id = k; - curr = SPLAY_FIND(_tree, &t->tree, &key); - if (curr == NULL) { - SPLAY_INSERT(_tree, &t->tree, &key); - curr = SPLAY_NEXT(_tree, &t->tree, &key); - SPLAY_REMOVE(_tree, &t->tree, &key); - } - } - } else - curr = SPLAY_NEXT(_tree, &t->tree, curr); - - if (curr) { - *hdl = curr; - if (id) - *id = curr->id; - if (data) - *data = curr->data; - return (1); - } - - return (0); -} - -void -tree_merge(struct tree *dst, struct tree *src) -{ - struct treeentry *entry; - - while (!SPLAY_EMPTY(&src->tree)) { - entry = SPLAY_ROOT(&src->tree); - SPLAY_REMOVE(_tree, &src->tree, entry); - if (SPLAY_INSERT(_tree, &dst->tree, entry)) - errx(1, "tree_merge: duplicate"); - } - dst->count += src->count; - src->count = 0; -} - -static int -treeentry_cmp(struct treeentry *a, struct treeentry *b) -{ - if (a->id < b->id) - return (-1); - if (a->id > b->id) - return (1); - return (0); -} - -SPLAY_GENERATE(_tree, treeentry, entry, treeentry_cmp); diff --git a/smtpd/tree.h b/smtpd/tree.h deleted file mode 100644 index 3d719f09..00000000 --- a/smtpd/tree.h +++ /dev/null @@ -1,48 +0,0 @@ -/* $OpenBSD: tree.h,v 1.1 2018/12/23 16:06:24 gilles Exp $ */ - -/* - * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _TREE_H_ -#define _TREE_H_ - -SPLAY_HEAD(_tree, treeentry); - -struct tree { - struct _tree tree; - size_t count; -}; - - -/* tree.c */ -#define tree_init(t) do { SPLAY_INIT(&((t)->tree)); (t)->count = 0; } while(0) -#define tree_empty(t) SPLAY_EMPTY(&((t)->tree)) -#define tree_count(t) ((t)->count) -int tree_check(struct tree *, uint64_t); -void *tree_set(struct tree *, uint64_t, void *); -void tree_xset(struct tree *, uint64_t, void *); -void *tree_get(struct tree *, uint64_t); -void *tree_xget(struct tree *, uint64_t); -void *tree_pop(struct tree *, uint64_t); -void *tree_xpop(struct tree *, uint64_t); -int tree_poproot(struct tree *, uint64_t *, void **); -int tree_root(struct tree *, uint64_t *, void **); -int tree_iter(struct tree *, void **, uint64_t *, void **); -int tree_iterfrom(struct tree *, void **, uint64_t, uint64_t *, void **); -void tree_merge(struct tree *, struct tree *); - -#endif diff --git a/smtpd/unpack_dns.c b/smtpd/unpack_dns.c deleted file mode 100644 index 974d5727..00000000 --- a/smtpd/unpack_dns.c +++ /dev/null @@ -1,300 +0,0 @@ -/* $OpenBSD: unpack_dns.c,v 1.1 2018/01/06 07:57:53 sunil Exp $ */ - -/* - * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include <arpa/nameser_compat.h> -#endif -#include <arpa/inet.h> - -#include <string.h> - -#include "unpack_dns.h" - -static int unpack_data(struct unpack *, void *, size_t); -static int unpack_u16(struct unpack *, uint16_t *); -static int unpack_u32(struct unpack *, uint32_t *); -static int unpack_inaddr(struct unpack *, struct in_addr *); -static int unpack_in6addr(struct unpack *, struct in6_addr *); -static int unpack_dname(struct unpack *, char *, size_t); - -void -unpack_init(struct unpack *unpack, const char *buf, size_t len) -{ - unpack->buf = buf; - unpack->len = len; - unpack->offset = 0; - unpack->err = NULL; -} - -int -unpack_header(struct unpack *p, struct dns_header *h) -{ - if (unpack_data(p, h, HFIXEDSZ) == -1) - return (-1); - - h->flags = ntohs(h->flags); - h->qdcount = ntohs(h->qdcount); - h->ancount = ntohs(h->ancount); - h->nscount = ntohs(h->nscount); - h->arcount = ntohs(h->arcount); - - return (0); -} - -int -unpack_query(struct unpack *p, struct dns_query *q) -{ - unpack_dname(p, q->q_dname, sizeof(q->q_dname)); - unpack_u16(p, &q->q_type); - unpack_u16(p, &q->q_class); - - return (p->err) ? (-1) : (0); -} - -int -unpack_rr(struct unpack *p, struct dns_rr *rr) -{ - uint16_t rdlen; - size_t save_offset; - - unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); - unpack_u16(p, &rr->rr_type); - unpack_u16(p, &rr->rr_class); - unpack_u32(p, &rr->rr_ttl); - unpack_u16(p, &rdlen); - - if (p->err) - return (-1); - - if (p->len - p->offset < rdlen) { - p->err = "too short"; - return (-1); - } - - save_offset = p->offset; - - switch (rr->rr_type) { - - case T_CNAME: - unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); - break; - - case T_MX: - unpack_u16(p, &rr->rr.mx.preference); - unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); - break; - - case T_NS: - unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); - break; - - case T_PTR: - unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); - break; - - case T_SOA: - unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); - unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); - unpack_u32(p, &rr->rr.soa.serial); - unpack_u32(p, &rr->rr.soa.refresh); - unpack_u32(p, &rr->rr.soa.retry); - unpack_u32(p, &rr->rr.soa.expire); - unpack_u32(p, &rr->rr.soa.minimum); - break; - - case T_A: - if (rr->rr_class != C_IN) - goto other; - unpack_inaddr(p, &rr->rr.in_a.addr); - break; - - case T_AAAA: - if (rr->rr_class != C_IN) - goto other; - unpack_in6addr(p, &rr->rr.in_aaaa.addr6); - break; - default: - other: - rr->rr.other.rdata = p->buf + p->offset; - rr->rr.other.rdlen = rdlen; - p->offset += rdlen; - } - - if (p->err) - return (-1); - - /* make sure that the advertised rdlen is really ok */ - if (p->offset - save_offset != rdlen) - p->err = "bad dlen"; - - return (p->err) ? (-1) : (0); -} - -ssize_t -dname_expand(const unsigned char *data, size_t len, size_t offset, - size_t *newoffset, char *dst, size_t max) -{ - size_t n, count, end, ptr, start; - ssize_t res; - - if (offset >= len) - return (-1); - - res = 0; - end = start = offset; - - for (; (n = data[offset]); ) { - if ((n & 0xc0) == 0xc0) { - if (offset + 2 > len) - return (-1); - ptr = 256 * (n & ~0xc0) + data[offset + 1]; - if (ptr >= start) - return (-1); - if (end < offset + 2) - end = offset + 2; - offset = start = ptr; - continue; - } - if (offset + n + 1 > len) - return (-1); - - /* copy n + at offset+1 */ - if (dst != NULL && max != 0) { - count = (max < n + 1) ? (max) : (n + 1); - memmove(dst, data + offset, count); - dst += count; - max -= count; - } - res += n + 1; - offset += n + 1; - if (end < offset) - end = offset; - } - if (end < offset + 1) - end = offset + 1; - - if (dst != NULL && max != 0) - dst[0] = 0; - if (newoffset) - *newoffset = end; - return (res + 1); -} - -char * -print_dname(const char *_dname, char *buf, size_t max) -{ - const unsigned char *dname = _dname; - char *res; - size_t left, n, count; - - if (_dname[0] == 0) { - (void)strlcpy(buf, ".", max); - return buf; - } - - res = buf; - left = max - 1; - for (n = 0; dname[0] && left; n += dname[0]) { - count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); - memmove(buf, dname + 1, count); - dname += dname[0] + 1; - left -= count; - buf += count; - if (left) { - left -= 1; - *buf++ = '.'; - } - } - buf[0] = 0; - - return (res); -} - -static int -unpack_data(struct unpack *p, void *data, size_t len) -{ - if (p->err) - return (-1); - - if (p->len - p->offset < len) { - p->err = "too short"; - return (-1); - } - - memmove(data, p->buf + p->offset, len); - p->offset += len; - - return (0); -} - -static int -unpack_u16(struct unpack *p, uint16_t *u16) -{ - if (unpack_data(p, u16, 2) == -1) - return (-1); - - *u16 = ntohs(*u16); - - return (0); -} - -static int -unpack_u32(struct unpack *p, uint32_t *u32) -{ - if (unpack_data(p, u32, 4) == -1) - return (-1); - - *u32 = ntohl(*u32); - - return (0); -} - -static int -unpack_inaddr(struct unpack *p, struct in_addr *a) -{ - return (unpack_data(p, a, 4)); -} - -static int -unpack_in6addr(struct unpack *p, struct in6_addr *a6) -{ - return (unpack_data(p, a6, 16)); -} - -static int -unpack_dname(struct unpack *p, char *dst, size_t max) -{ - ssize_t e; - - if (p->err) - return (-1); - - e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max); - if (e == -1) { - p->err = "bad domain name"; - return (-1); - } - if (e < 0 || e > MAXDNAME) { - p->err = "domain name too long"; - return (-1); - } - - return (0); -} diff --git a/smtpd/unpack_dns.h b/smtpd/unpack_dns.h deleted file mode 100644 index 2318a0c5..00000000 --- a/smtpd/unpack_dns.h +++ /dev/null @@ -1,96 +0,0 @@ -/* $OpenBSD: unpack_dns.h,v 1.1 2018/01/06 07:57:53 sunil Exp $ */ - -/* - * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> - -#include <netinet/in.h> - -#include <arpa/inet.h> -#include <arpa/nameser.h> - -struct unpack { - const char *buf; - size_t len; - size_t offset; - const char *err; -}; - -struct dns_header { - uint16_t id; - uint16_t flags; - uint16_t qdcount; - uint16_t ancount; - uint16_t nscount; - uint16_t arcount; -}; - -struct dns_query { - char q_dname[MAXDNAME]; - uint16_t q_type; - uint16_t q_class; -}; - -struct dns_rr { - char rr_dname[MAXDNAME]; - uint16_t rr_type; - uint16_t rr_class; - uint32_t rr_ttl; - union { - struct { - char cname[MAXDNAME]; - } cname; - struct { - uint16_t preference; - char exchange[MAXDNAME]; - } mx; - struct { - char nsname[MAXDNAME]; - } ns; - struct { - char ptrname[MAXDNAME]; - } ptr; - struct { - char mname[MAXDNAME]; - char rname[MAXDNAME]; - uint32_t serial; - uint32_t refresh; - uint32_t retry; - uint32_t expire; - uint32_t minimum; - } soa; - struct { - struct in_addr addr; - } in_a; - struct { - struct in6_addr addr6; - } in_aaaa; - struct { - uint16_t rdlen; - const void *rdata; - } other; - } rr; -}; - -void unpack_init(struct unpack *, const char *, size_t); -int unpack_header(struct unpack *, struct dns_header *); -int unpack_rr(struct unpack *, struct dns_rr *); -int unpack_query(struct unpack *, struct dns_query *); -char *print_dname(const char *, char *, size_t); -ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *, - char *, size_t); - diff --git a/smtpd/util.c b/smtpd/util.c deleted file mode 100644 index b2b1458c..00000000 --- a/smtpd/util.c +++ /dev/null @@ -1,870 +0,0 @@ -/* $OpenBSD: util.c,v 1.151 2020/02/24 23:54:28 millert Exp $ */ - -/* - * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <fts.h> -#include <imsg.h> -#include <inttypes.h> -#include <libgen.h> -#include <netdb.h> -#include <pwd.h> -#include <limits.h> -#include <resolv.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -const char *log_in6addr(const struct in6_addr *); -const char *log_sockaddr(struct sockaddr *); -static int parse_mailname_file(char *, size_t); - -int tracing = 0; -int foreground_log = 0; - -void * -xmalloc(size_t size) -{ - void *r; - - if ((r = malloc(size)) == NULL) - fatal("malloc"); - - return (r); -} - -void * -xcalloc(size_t nmemb, size_t size) -{ - void *r; - - if ((r = calloc(nmemb, size)) == NULL) - fatal("calloc"); - - return (r); -} - -char * -xstrdup(const char *str) -{ - char *r; - - if ((r = strdup(str)) == NULL) - fatal("strdup"); - - return (r); -} - -void * -xmemdup(const void *ptr, size_t size) -{ - void *r; - - if ((r = malloc(size)) == NULL) - fatal("malloc"); - - memmove(r, ptr, size); - - return (r); -} - -int -xasprintf(char **ret, const char *format, ...) -{ - int r; - va_list ap; - - va_start(ap, format); - r = vasprintf(ret, format, ap); - va_end(ap); - if (r == -1) - fatal("vasprintf"); - - return (r); -} - - -#if !defined(NO_IO) -int -io_xprintf(struct io *io, const char *fmt, ...) -{ - va_list ap; - int len; - - va_start(ap, fmt); - len = io_vprintf(io, fmt, ap); - va_end(ap); - if (len == -1) - fatal("io_xprintf(%p, %s, ...)", io, fmt); - - return len; -} - -int -io_xprint(struct io *io, const char *str) -{ - int len; - - len = io_print(io, str); - if (len == -1) - fatal("io_xprint(%p, %s, ...)", io, str); - - return len; -} -#endif - -char * -strip(char *s) -{ - size_t l; - - while (isspace((unsigned char)*s)) - s++; - - for (l = strlen(s); l; l--) { - if (!isspace((unsigned char)s[l-1])) - break; - s[l-1] = '\0'; - } - - return (s); -} - -int -bsnprintf(char *str, size_t size, const char *format, ...) -{ - int ret; - va_list ap; - - va_start(ap, format); - ret = vsnprintf(str, size, format, ap); - va_end(ap); - if (ret < 0 || ret >= (int)size) - return 0; - - return 1; -} - - -int -ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create) -{ - char mode_str[12]; - int ret; - struct stat sb; - - if (stat(path, &sb) == -1) { - if (errno != ENOENT || create == 0) { - log_warn("stat: %s", path); - return (0); - } - - /* chmod is deferred to avoid umask effect */ - if (mkdir(path, 0) == -1) { - log_warn("mkdir: %s", path); - return (0); - } - - if (chown(path, owner, group) == -1) { - log_warn("chown: %s", path); - return (0); - } - - if (chmod(path, mode) == -1) { - log_warn("chmod: %s", path); - return (0); - } - - if (stat(path, &sb) == -1) { - log_warn("stat: %s", path); - return (0); - } - } - - ret = 1; - - /* check if it's a directory */ - if (!S_ISDIR(sb.st_mode)) { - ret = 0; - log_warnx("%s is not a directory", path); - } - - /* check that it is owned by owner/group */ - if (sb.st_uid != owner) { - ret = 0; - log_warnx("%s is not owned by uid %d", path, owner); - } - if (sb.st_gid != group) { - ret = 0; - log_warnx("%s is not owned by gid %d", path, group); - } - - /* check permission */ - if ((sb.st_mode & 07777) != mode) { - ret = 0; - strmode(mode, mode_str); - mode_str[10] = '\0'; - log_warnx("%s must be %s (%o)", path, mode_str + 1, mode); - } - - return ret; -} - -int -rmtree(char *path, int keepdir) -{ - char *path_argv[2]; - FTS *fts; - FTSENT *e; - int ret, depth; - - path_argv[0] = path; - path_argv[1] = NULL; - ret = 0; - depth = 0; - - fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); - if (fts == NULL) { - log_warn("fts_open: %s", path); - return (-1); - } - - while ((e = fts_read(fts)) != NULL) { - switch (e->fts_info) { - case FTS_D: - depth++; - break; - case FTS_DP: - case FTS_DNR: - depth--; - if (keepdir && depth == 0) - continue; - if (rmdir(e->fts_path) == -1) { - log_warn("rmdir: %s", e->fts_path); - ret = -1; - } - break; - - case FTS_F: - if (unlink(e->fts_path) == -1) { - log_warn("unlink: %s", e->fts_path); - ret = -1; - } - } - } - - fts_close(fts); - - return (ret); -} - -int -mvpurge(char *from, char *to) -{ - size_t n; - int retry; - const char *sep; - char buf[PATH_MAX]; - - if ((n = strlen(to)) == 0) - fatalx("to is empty"); - - sep = (to[n - 1] == '/') ? "" : "/"; - retry = 0; - -again: - (void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random()); - if (rename(from, buf) == -1) { - /* ENOTDIR has actually 2 meanings, and incorrect input - * could lead to an infinite loop. Consider that after - * 20 tries something is hopelessly wrong. - */ - if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) { - if ((retry++) >= 20) - return (-1); - goto again; - } - return -1; - } - - return 0; -} - - -int -mktmpfile(void) -{ - char path[PATH_MAX]; - int fd; - - if (!bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX", - PATH_TEMPORARY)) { - log_warn("snprintf"); - fatal("exiting"); - } - - if ((fd = mkstemp(path)) == -1) { - log_warn("cannot create temporary file %s", path); - fatal("exiting"); - } - unlink(path); - return (fd); -} - - -/* Close file, signifying temporary error condition (if any) to the caller. */ -int -safe_fclose(FILE *fp) -{ - if (ferror(fp)) { - fclose(fp); - return 0; - } - if (fflush(fp)) { - fclose(fp); - if (errno == ENOSPC) - return 0; - fatal("safe_fclose: fflush"); - } - if (fsync(fileno(fp))) - fatal("safe_fclose: fsync"); - if (fclose(fp)) - fatal("safe_fclose: fclose"); - - return 1; -} - -int -hostname_match(const char *hostname, const char *pattern) -{ - while (*pattern != '\0' && *hostname != '\0') { - if (*pattern == '*') { - while (*pattern == '*') - pattern++; - while (*hostname != '\0' && - tolower((unsigned char)*hostname) != - tolower((unsigned char)*pattern)) - hostname++; - continue; - } - - if (tolower((unsigned char)*pattern) != - tolower((unsigned char)*hostname)) - return 0; - pattern++; - hostname++; - } - - return (*hostname == '\0' && *pattern == '\0'); -} - -int -mailaddr_match(const struct mailaddr *maddr1, const struct mailaddr *maddr2) -{ - struct mailaddr m1 = *maddr1; - struct mailaddr m2 = *maddr2; - char *p; - - /* catchall */ - if (m2.user[0] == '\0' && m2.domain[0] == '\0') - return 1; - - if (m2.domain[0] && !hostname_match(m1.domain, m2.domain)) - return 0; - - if (m2.user[0]) { - /* if address from table has a tag, we must respect it */ - if (strchr(m2.user, *env->sc_subaddressing_delim) == NULL) { - /* otherwise, strip tag from session address if any */ - p = strchr(m1.user, *env->sc_subaddressing_delim); - if (p) - *p = '\0'; - } - if (strcasecmp(m1.user, m2.user)) - return 0; - } - return 1; -} - -int -valid_localpart(const char *s) -{ -#define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c))) -nextatom: - if (!IS_ATEXT(*s) || *s == '\0') - return 0; - while (*(++s) != '\0') { - if (*s == '.') - break; - if (IS_ATEXT(*s)) - continue; - return 0; - } - if (*s == '.') { - s++; - goto nextatom; - } - return 1; -} - -int -valid_domainpart(const char *s) -{ - struct in_addr ina; - struct in6_addr ina6; - char *c, domain[SMTPD_MAXDOMAINPARTSIZE]; - const char *p; - size_t dlen; - - if (*s == '[') { - if (strncasecmp("[IPv6:", s, 6) == 0) - p = s + 6; - else - p = s + 1; - - if (strlcpy(domain, p, sizeof domain) >= sizeof domain) - return 0; - - c = strchr(domain, ']'); - if (!c || c[1] != '\0') - return 0; - - *c = '\0'; - - if (inet_pton(AF_INET6, domain, &ina6) == 1) - return 1; - if (inet_pton(AF_INET, domain, &ina) == 1) - return 1; - - return 0; - } - - if (*s == '\0') - return 0; - - dlen = strlen(s); - if (dlen >= sizeof domain) - return 0; - - if (s[dlen - 1] == '.') - return 0; - - return res_hnok(s); -} - -#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((unsigned char)(c)) || isdigit((unsigned char)(c))) -#define LABELMAX 63 -#define DNAMEMAX 253 - -int -valid_domainname(const char *str) -{ - const char *label, *s; - - /* - * Expect a sequence of dot-separated labels, possibly with a trailing - * dot. The empty string is rejected, as well a single dot. - */ - for (s = str; *s; s++) { - - /* Start of a new label. */ - label = s; - while (LABELCHR(*s)) - s++; - - /* Must have at least one char and at most LABELMAX. */ - if (s == label || s - label > LABELMAX) - return 0; - - /* If last label, stop here. */ - if (*s == '\0') - break; - - /* Expect a dot as label separator or last char. */ - if (*s != '.') - return 0; - } - - /* Must have at leat one label and no more than DNAMEMAX chars. */ - if (s == str || s - str > DNAMEMAX) - return 0; - - return 1; -} - -int -valid_smtp_response(const char *s) -{ - if (strlen(s) < 5) - return 0; - - if ((s[0] < '2' || s[0] > '5') || - (s[1] < '0' || s[1] > '9') || - (s[2] < '0' || s[2] > '9') || - (s[3] != ' ')) - return 0; - - return 1; -} - -int -secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread) -{ - char buf[PATH_MAX]; - char homedir[PATH_MAX]; - struct stat st; - char *cp; - - if (realpath(path, buf) == NULL) - return 0; - - if (realpath(userdir, homedir) == NULL) - homedir[0] = '\0'; - - /* Check the open file to avoid races. */ - if (fstat(fd, &st) == -1 || - !S_ISREG(st.st_mode) || - st.st_uid != uid || - (st.st_mode & (mayread ? 022 : 066)) != 0) - return 0; - - /* For each component of the canonical path, walking upwards. */ - for (;;) { - if ((cp = dirname(buf)) == NULL) - return 0; - (void)strlcpy(buf, cp, sizeof(buf)); - - if (stat(buf, &st) == -1 || - (st.st_uid != 0 && st.st_uid != uid) || - (st.st_mode & 022) != 0) - return 0; - - /* We can stop checking after reaching homedir level. */ - if (strcmp(homedir, buf) == 0) - break; - - /* - * dirname should always complete with a "/" path, - * but we can be paranoid and check for "." too - */ - if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) - break; - } - - return 1; -} - -void -addargs(arglist *args, char *fmt, ...) -{ - va_list ap; - char *cp; - uint nalloc; - int r; - char **tmp; - - va_start(ap, fmt); - r = vasprintf(&cp, fmt, ap); - va_end(ap); - if (r == -1) - fatal("addargs: argument too long"); - - nalloc = args->nalloc; - if (args->list == NULL) { - nalloc = 32; - args->num = 0; - } else if (args->num+2 >= nalloc) - nalloc *= 2; - - tmp = reallocarray(args->list, nalloc, sizeof(char *)); - if (tmp == NULL) - fatal("addargs: reallocarray"); - args->list = tmp; - args->nalloc = nalloc; - args->list[args->num++] = cp; - args->list[args->num] = NULL; -} - -int -lowercase(char *buf, const char *s, size_t len) -{ - if (len == 0) - return 0; - - if (strlcpy(buf, s, len) >= len) - return 0; - - while (*buf != '\0') { - *buf = tolower((unsigned char)*buf); - buf++; - } - - return 1; -} - -int -uppercase(char *buf, const char *s, size_t len) -{ - if (len == 0) - return 0; - - if (strlcpy(buf, s, len) >= len) - return 0; - - while (*buf != '\0') { - *buf = toupper((unsigned char)*buf); - buf++; - } - - return 1; -} - -void -xlowercase(char *buf, const char *s, size_t len) -{ - if (len == 0) - fatalx("lowercase: len == 0"); - - if (!lowercase(buf, s, len)) - fatalx("lowercase: truncation"); -} - -uint64_t -generate_uid(void) -{ - static uint32_t id; - static uint8_t inited; - uint64_t uid; - - if (!inited) { - id = arc4random(); - inited = 1; - } - while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0) - ; - - return (uid); -} - -int -session_socket_error(int fd) -{ - int error; - socklen_t len; - - len = sizeof(error); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) - fatal("session_socket_error: getsockopt"); - - return (error); -} - -const char * -parse_smtp_response(char *line, size_t len, char **msg, int *cont) -{ - if (len >= LINE_MAX) - return "line too long"; - - if (len > 3) { - if (msg) - *msg = line + 4; - if (cont) - *cont = (line[3] == '-'); - } else if (len == 3) { - if (msg) - *msg = line + 3; - if (cont) - *cont = 0; - } else - return "line too short"; - - /* validate reply code */ - if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) || - !isdigit((unsigned char)line[2])) - return "reply code out of range"; - - return NULL; -} - -static int -parse_mailname_file(char *hostname, size_t len) -{ - FILE *fp; - char *buf = NULL; - size_t bufsz = 0; - ssize_t buflen; - - if ((fp = fopen(MAILNAME_FILE, "r")) == NULL) - return 1; - - if ((buflen = getline(&buf, &bufsz, fp)) == -1) - goto error; - - if (buf[buflen - 1] == '\n') - buf[buflen - 1] = '\0'; - - if (strlcpy(hostname, buf, len) >= len) { - fprintf(stderr, MAILNAME_FILE " entry too long"); - goto error; - } - - return 0; -error: - fclose(fp); - free(buf); - return 1; -} - -int -getmailname(char *hostname, size_t len) -{ - struct addrinfo hints, *res = NULL; - int error; - - /* Try MAILNAME_FILE first */ - if (parse_mailname_file(hostname, len) == 0) - return 0; - - /* Next, gethostname(3) */ - if (gethostname(hostname, len) == -1) { - fprintf(stderr, "getmailname: gethostname() failed\n"); - return -1; - } - - if (strchr(hostname, '.') != NULL) - return 0; - - /* Canonicalize if domain part is missing */ - memset(&hints, 0, sizeof hints); - hints.ai_family = PF_UNSPEC; - hints.ai_flags = AI_CANONNAME; - error = getaddrinfo(hostname, NULL, &hints, &res); - if (error) - return 0; /* Continue with non-canon hostname */ - - if (strlcpy(hostname, res->ai_canonname, len) >= len) { - fprintf(stderr, "hostname too long"); - freeaddrinfo(res); - return -1; - } - - freeaddrinfo(res); - return 0; -} - -int -base64_encode(unsigned char const *src, size_t srclen, - char *dest, size_t destsize) -{ - return __b64_ntop(src, srclen, dest, destsize); -} - -int -base64_decode(char const *src, unsigned char *dest, size_t destsize) -{ - return __b64_pton(src, dest, destsize); -} - -int -base64_encode_rfc3548(unsigned char const *src, size_t srclen, - char *dest, size_t destsize) -{ - size_t i; - int ret; - - if ((ret = base64_encode(src, srclen, dest, destsize)) == -1) - return -1; - - for (i = 0; i < destsize; ++i) { - if (dest[i] == '/') - dest[i] = '_'; - else if (dest[i] == '+') - dest[i] = '-'; - } - - return ret; -} - -void -log_trace(int mask, const char *emsg, ...) -{ - va_list ap; - - if (tracing & mask) { - va_start(ap, emsg); - vlog(LOG_DEBUG, emsg, ap); - va_end(ap); - } -} - -void -log_trace_verbose(int v) -{ - tracing = v; - - /* Set debug logging in log.c */ - log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log); -} - -void -xclosefrom(int lowfd) -{ -#if defined HAVE_CLOSEFROM_INT - if (closefrom(lowfd) == -1) - err(1, "closefrom"); -#else - closefrom(lowfd); -#endif -} - -void -portable_freeaddrinfo(struct addrinfo *ai) -{ - struct addrinfo *p; - - do { - p = ai; - ai = ai->ai_next; - free(p->ai_canonname); - free(p); - } while (ai); -} diff --git a/smtpd/waitq.c b/smtpd/waitq.c deleted file mode 100644 index 082a1e51..00000000 --- a/smtpd/waitq.c +++ /dev/null @@ -1,104 +0,0 @@ -/* $OpenBSD: waitq.c,v 1.6 2018/05/31 21:06:12 gilles Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/uio.h> - -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> - -#include "smtpd.h" - -struct waiter { - TAILQ_ENTRY(waiter) entry; - void (*cb)(void *, void *, void *); - void *arg; -}; - -struct waitq { - SPLAY_ENTRY(waitq) entry; - void *tag; - TAILQ_HEAD(, waiter) waiters; -}; - -static int waitq_cmp(struct waitq *, struct waitq *); - -SPLAY_HEAD(waitqtree, waitq); -SPLAY_PROTOTYPE(waitqtree, waitq, entry, waitq_cmp); - -static struct waitqtree waitqs = SPLAY_INITIALIZER(&waitqs); - -static int -waitq_cmp(struct waitq *a, struct waitq *b) -{ - if (a->tag < b->tag) - return (-1); - if (a->tag > b->tag) - return (1); - return (0); -} - -SPLAY_GENERATE(waitqtree, waitq, entry, waitq_cmp); - -int -waitq_wait(void *tag, void (*cb)(void *, void *, void *), void *arg) -{ - struct waitq *wq, key; - struct waiter *w; - - key.tag = tag; - wq = SPLAY_FIND(waitqtree, &waitqs, &key); - if (wq == NULL) { - wq = xmalloc(sizeof *wq); - wq->tag = tag; - TAILQ_INIT(&wq->waiters); - SPLAY_INSERT(waitqtree, &waitqs, wq); - } - - w = xmalloc(sizeof *w); - w->cb = cb; - w->arg = arg; - TAILQ_INSERT_TAIL(&wq->waiters, w, entry); - - return (w == TAILQ_FIRST(&wq->waiters)); -} - -void -waitq_run(void *tag, void *result) -{ - struct waitq *wq, key; - struct waiter *w; - - key.tag = tag; - wq = SPLAY_FIND(waitqtree, &waitqs, &key); - SPLAY_REMOVE(waitqtree, &waitqs, wq); - - while ((w = TAILQ_FIRST(&wq->waiters))) { - TAILQ_REMOVE(&wq->waiters, w, entry); - w->cb(tag, w->arg, result); - free(w); - } - free(wq); -} |