diff options
-rw-r--r-- | usr.sbin/smtpd/lka_session.c | 320 | ||||
-rw-r--r-- | usr.sbin/smtpd/mda_variables.c | 354 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 7 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd/Makefile | 3 |
4 files changed, 364 insertions, 320 deletions
diff --git a/usr.sbin/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c index e70f359df3d..d12b3c56fda 100644 --- a/usr.sbin/smtpd/lka_session.c +++ b/usr.sbin/smtpd/lka_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka_session.c,v 1.80 2016/08/31 10:18:08 gilles Exp $ */ +/* $OpenBSD: lka_session.c,v 1.81 2017/05/26 21:30:00 gilles Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> @@ -66,28 +66,10 @@ static void lka_expand(struct lka_session *, struct rule *, static void lka_submit(struct lka_session *, struct rule *, struct expandnode *); static void lka_resume(struct lka_session *); -static size_t lka_expand_format(char *, size_t, const struct envelope *, - const struct userinfo *); - -static int mod_lowercase(char *, size_t); -static int mod_uppercase(char *, size_t); -static int mod_strip(char *, size_t); - -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 */ -}; static int init; static struct tree sessions; -#define MAXTOKENLEN 128 - void lka_session(uint64_t id, struct envelope *envelope) { @@ -570,7 +552,7 @@ lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) else fatalx("lka_deliver: bad node type"); - r = lka_expand_format(ep->agent.mda.buffer, + r = mda_expand_format(ep->agent.mda.buffer, sizeof(ep->agent.mda.buffer), ep, &lk.userinfo); if (!r) { lks->error = LKA_TEMPFAIL; @@ -586,301 +568,3 @@ lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry); } - - -static size_t -lka_expand_token(char *dest, size_t len, const char *token, - const struct envelope *ep, const struct userinfo *ui) -{ - 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 0; - - /* 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 0; - - *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 0; - - /* 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", - ep->sender.user, ep->sender.domain) >= (int)sizeof tmp) - return 0; - string = tmp; - } - else if (!strcasecmp("dest", rtoken)) { - if (snprintf(tmp, sizeof tmp, "%s@%s", - ep->dest.user, ep->dest.domain) >= (int)sizeof tmp) - return 0; - string = tmp; - } - else if (!strcasecmp("rcpt", rtoken)) { - if (snprintf(tmp, sizeof tmp, "%s@%s", - ep->rcpt.user, ep->rcpt.domain) >= (int)sizeof tmp) - return 0; - string = tmp; - } - else if (!strcasecmp("sender.user", rtoken)) - string = ep->sender.user; - else if (!strcasecmp("sender.domain", rtoken)) - string = ep->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("dest.user", rtoken)) - string = ep->dest.user; - else if (!strcasecmp("dest.domain", rtoken)) - string = ep->dest.domain; - else if (!strcasecmp("rcpt.user", rtoken)) - string = ep->rcpt.user; - else if (!strcasecmp("rcpt.domain", rtoken)) - string = ep->rcpt.domain; - else - return 0; - - if (string != tmp) { - if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp) - return 0; - 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 0; /* modifier error */ - break; - } - } - if ((size_t)i == nitems(token_modifiers)) - return 0; /* 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 0; - - /* 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 0; - 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 0; - - string += begoff; - for (; i; i--) { - *dest = (replace && *string == '/') ? ':' : *string; - dest++; - string++; - } - - return endoff - begoff; -} - - -static size_t -lka_expand_format(char *buf, size_t len, const struct envelope *ep, - const struct userinfo *ui) -{ - char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; - char exptok[EXPAND_BUFFER]; - size_t exptoklen; - char token[MAXTOKENLEN]; - size_t ret, tmpret; - - if (len < sizeof tmpbuf) { - log_warnx("lka_expand_format: tmp buffer < rule buffer"); - return 0; - } - - 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 = lka_expand_token(exptok, sizeof exptok, token, ep, - ui); - if (exptoklen == 0) - return 0; - - /* writing expanded token at ptmp will overflow tmpbuf */ - if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= exptoklen) - return 0; - - memcpy(ptmp, exptok, exptoklen); - pbuf = ebuf + 1; - ptmp += exptoklen; - tmpret = exptoklen; - } - if (ret >= sizeof tmpbuf) - return 0; - - if ((ret = strlcpy(buf, tmpbuf, len)) >= len) - return 0; - - 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/usr.sbin/smtpd/mda_variables.c b/usr.sbin/smtpd/mda_variables.c new file mode 100644 index 00000000000..aa30e047724 --- /dev/null +++ b/usr.sbin/smtpd/mda_variables.c @@ -0,0 +1,354 @@ +/* $OpenBSD: mda_variables.c,v 1.1 2017/05/26 21:30:00 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 <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 + +size_t mda_expand_format(char *, size_t, const struct envelope *, + const struct userinfo *); +static size_t mda_expand_token(char *, size_t, const char *, + const struct envelope *, const struct userinfo *); +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 size_t +mda_expand_token(char *dest, size_t len, const char *token, + const struct envelope *ep, const struct userinfo *ui) +{ + 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 0; + + /* 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 0; + + *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 0; + + /* 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", + ep->sender.user, ep->sender.domain) >= (int)sizeof tmp) + return 0; + string = tmp; + } + else if (!strcasecmp("dest", rtoken)) { + if (snprintf(tmp, sizeof tmp, "%s@%s", + ep->dest.user, ep->dest.domain) >= (int)sizeof tmp) + return 0; + string = tmp; + } + else if (!strcasecmp("rcpt", rtoken)) { + if (snprintf(tmp, sizeof tmp, "%s@%s", + ep->rcpt.user, ep->rcpt.domain) >= (int)sizeof tmp) + return 0; + string = tmp; + } + else if (!strcasecmp("sender.user", rtoken)) + string = ep->sender.user; + else if (!strcasecmp("sender.domain", rtoken)) + string = ep->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("dest.user", rtoken)) + string = ep->dest.user; + else if (!strcasecmp("dest.domain", rtoken)) + string = ep->dest.domain; + else if (!strcasecmp("rcpt.user", rtoken)) + string = ep->rcpt.user; + else if (!strcasecmp("rcpt.domain", rtoken)) + string = ep->rcpt.domain; + else + return 0; + + if (string != tmp) { + if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp) + return 0; + 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 0; /* modifier error */ + break; + } + } + if ((size_t)i == nitems(token_modifiers)) + return 0; /* 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 0; + + /* 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 0; + 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 0; + + string += begoff; + for (; i; i--) { + *dest = (replace && *string == '/') ? ':' : *string; + dest++; + string++; + } + + return endoff - begoff; +} + + +size_t +mda_expand_format(char *buf, size_t len, const struct envelope *ep, + const struct userinfo *ui) +{ + char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; + char exptok[EXPAND_BUFFER]; + size_t exptoklen; + char token[MAXTOKENLEN]; + size_t ret, tmpret; + + if (len < sizeof tmpbuf) { + log_warnx("mda_expand_format: tmp buffer < rule buffer"); + return 0; + } + + 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, ep, + ui); + if (exptoklen == 0) + return 0; + + /* writing expanded token at ptmp will overflow tmpbuf */ + if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= exptoklen) + return 0; + + memcpy(ptmp, exptok, exptoklen); + pbuf = ebuf + 1; + ptmp += exptoklen; + tmpret = exptoklen; + } + if (ret >= sizeof tmpbuf) + return 0; + + if ((ret = strlcpy(buf, tmpbuf, len)) >= len) + return 0; + + 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/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index a489515675e..f158efa73d5 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.531 2017/05/22 13:43:15 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.532 2017/05/26 21:30:00 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -1248,6 +1248,11 @@ void mda_postprivdrop(void); void mda_imsg(struct mproc *, struct imsg *); +/* mda_variables.c */ +size_t mda_expand_format(char *, size_t, const struct envelope *, + const struct userinfo *); + + /* makemap.c */ int makemap(int, char **); diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index 314fe2a58e7..a1d1f1d7d4b 100644 --- a/usr.sbin/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.86 2017/05/22 13:43:15 gilles Exp $ +# $OpenBSD: Makefile,v 1.87 2017/05/26 21:30:00 gilles Exp $ .PATH: ${.CURDIR}/.. @@ -27,6 +27,7 @@ SRCS+= lka_session.c SRCS+= log.c SRCS+= mailaddr.c SRCS+= mda.c +SRCS+= mda_variables.c SRCS+= mproc.c SRCS+= mta.c SRCS+= mta_session.c |