untrusted comment: verify with openbsd-78-base.pub RWS3/nvFmk4SWfnvIf31pQVm771eYAAkG/V9vAL9c/TyZ5gLcuePJoi1sH0Ehx4SCwTiEJDaWWoaHWlydMctivKs5eURs1XStAU= OpenBSD 7.8 errata 051, September 14, 2026: In smtpd(8) a custom MDA could execute arbitrary code as an unprivileged recipient user. Apply by doing: signify -Vep /etc/signify/openbsd-78-base.pub -x 051_smtpd.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install smtpd cd /usr/src/usr.sbin/smtpd make obj make make install Index: usr.sbin/smtpd/mda_unpriv.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/mda_unpriv.c,v diff -u -p -u -r1.9 mda_unpriv.c --- usr.sbin/smtpd/mda_unpriv.c 15 Mar 2024 21:52:20 -0000 1.9 +++ usr.sbin/smtpd/mda_unpriv.c 6 Sep 2026 17:57:48 -0000 @@ -23,11 +23,30 @@ #include "smtpd.h" +static void +mda_escape(char *dst, size_t dstsz, const char *src) +{ + size_t i; + + if (strlcpy(dst, src, dstsz) >= dstsz) + errx(1, "mda environment value too long"); + for (i = 0; dst[i] != '\0'; i++) + if (strchr(MAILADDR_RAW_ESCAPE, dst[i])) + dst[i] = ':'; +} + void mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, const char *pw_name, const char *pw_dir) { int idx; + char s_user[SMTPD_MAXLOCALPARTSIZE]; + char s_domain[SMTPD_MAXDOMAINPARTSIZE]; + char d_user[SMTPD_MAXLOCALPARTSIZE]; + char d_domain[SMTPD_MAXDOMAINPARTSIZE]; + char r_user[SMTPD_MAXLOCALPARTSIZE]; + char r_domain[SMTPD_MAXDOMAINPARTSIZE]; + char subaddr[SMTPD_SUBADDRESS_SIZE]; char *mda_environ[12]; char mda_exec[LINE_MAX]; char mda_wrapper[LINE_MAX]; @@ -49,26 +68,34 @@ mda_unpriv(struct dispatcher *dsp, struc mda_command = mda_exec; + mda_escape(r_user, sizeof r_user, deliver->rcpt.user); + mda_escape(r_domain, sizeof r_domain, deliver->rcpt.domain); + mda_escape(d_user, sizeof d_user, deliver->dest.user); + mda_escape(d_domain, sizeof d_domain, deliver->dest.domain); + mda_escape(s_user, sizeof s_user, deliver->sender.user); + mda_escape(s_domain, sizeof s_domain, deliver->sender.domain); + mda_escape(subaddr, sizeof subaddr, deliver->mda_subaddress); + /* 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++], "DOMAIN=%s", r_domain); xasprintf(&mda_environ[idx++], "HOME=%s", pw_dir); - xasprintf(&mda_environ[idx++], "ORIGINAL_RECIPIENT=%s@%s", deliver->rcpt.user, deliver->rcpt.domain); - xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", deliver->dest.user, deliver->dest.domain); + xasprintf(&mda_environ[idx++], "ORIGINAL_RECIPIENT=%s@%s", r_user, r_domain); + xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", d_user, d_domain); xasprintf(&mda_environ[idx++], "SHELL=/bin/sh"); - xasprintf(&mda_environ[idx++], "LOCAL=%s", deliver->rcpt.user); + xasprintf(&mda_environ[idx++], "LOCAL=%s", r_user); xasprintf(&mda_environ[idx++], "LOGNAME=%s", deliver->userinfo.username); xasprintf(&mda_environ[idx++], "USER=%s", deliver->userinfo.username); if (deliver->sender.user[0]) xasprintf(&mda_environ[idx++], "SENDER=%s@%s", - deliver->sender.user, deliver->sender.domain); + s_user, s_domain); else xasprintf(&mda_environ[idx++], "SENDER="); if (deliver->mda_subaddress[0]) - xasprintf(&mda_environ[idx++], "EXTENSION=%s", deliver->mda_subaddress); + xasprintf(&mda_environ[idx++], "EXTENSION=%s", subaddr); mda_environ[idx++] = (char *)NULL; Index: usr.sbin/smtpd/mda_variables.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/mda_variables.c,v diff -u -p -u -r1.10 mda_variables.c --- usr.sbin/smtpd/mda_variables.c 23 Apr 2024 13:34:51 -0000 1.10 +++ usr.sbin/smtpd/mda_variables.c 6 Sep 2026 17:57:48 -0000 @@ -53,6 +53,7 @@ mda_expand_token(char *dest, size_t len, char tmp[EXPAND_BUFFER]; const char *string = NULL; char *lbracket, *rbracket, *content, *sep, *mods; + const char *escape = NULL; ssize_t i; ssize_t begoff, endoff; const char *errstr = NULL; @@ -187,10 +188,13 @@ mda_expand_token(char *dest, size_t len, } while ((mods = sep) != NULL); } - if (!raw && replace) + if (replace) { + escape = raw ? MAILADDR_RAW_ESCAPE : MAILADDR_ESCAPE; + for (i = 0; (size_t)i < strlen(tmp); ++i) - if (strchr(MAILADDR_ESCAPE, tmp[i])) - tmp[i] = ':'; + if (strchr(escape, tmp[i])) + tmp[i] = ':'; + } /* expanded string is empty */ i = strlen(string); Index: usr.sbin/smtpd/smtpd.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v diff -u -p -u -r1.689 smtpd.h --- usr.sbin/smtpd/smtpd.h 8 Apr 2025 17:35:01 -0000 1.689 +++ usr.sbin/smtpd/smtpd.h 6 Sep 2026 17:57:48 -0000 @@ -70,11 +70,16 @@ /* * RFC 5322 defines these characters as valid, some of them are - * potentially dangerous and need to be escaped. + * potentially dangerous and need to be escaped. Even though we + * should be accepting $ ` { | } as valid characters, these are + * never present in legitimate envelope addresses and are often + * used in exploit attempts so we disallow them. */ -#define MAILADDR_ALLOWED "!#$%&'*/?^`{|}~+-=_" +#define MAILADDR_ALLOWED "!#%&'*/?^~+-=_" #define MAILADDR_ESCAPE "!#$%&'*?`{|}~" +/* filter out shell metacharacters but retain punctuation */ +#define MAILADDR_RAW_ESCAPE "!#$&'*?`{|}~" #define F_STARTTLS 0x01 #define F_SMTPS 0x02