[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#989324: unblock: opendmarc/1.4.0~beta1+dfsg-4



Control: tags -1 moreinfo

On 2021-06-01 10:34:59, David Bürgin wrote:
> Package: release.debian.org
> Severity: normal
> User: release.debian.org@packages.debian.org
> Usertags: unblock
> 
> Please unblock package opendmarc
> 
> Fixes for three CVEs have recently been released by the OpenDMARC
> developers. The fixes themselves are spread over more than a dozen
> commits, so I referred to the final code from upstream version 1.4.1.1.
> Detailed description of the CVEs and their resolution can be found at:
> https://github.com/trusteddomainproject/OpenDMARC/tree/rel-opendmarc-1-4-1-1/SECURITY
> 
> [ Reason ]
> Fixes for several CVEs have been released in OpenDMARC 1.4.1.1.
> 
> [ Impact ]
> Current opendmarc 1.4.0~beta1+dfsg-3 contains vulnerabilities that need
> patching.
> 
> [ Tests ]
> Version 1.4.0~beta1+dfsg-4 is running ‘in production’ (on my small
> personal mail server), rudimentary manual testing.
> 
> [ Risks ]
> The patches are not small but relatively self-contained and easy to
> read.
> 
> [ Checklist ]
>   [x] all changes are documented in the d/changelog
>   [x] I reviewed all changes and I approve them
>   [x] attach debdiff against the package in testing
> 
> unblock opendmarc/1.4.0~beta1+dfsg-4

> diff -Nru opendmarc-1.4.0~beta1+dfsg/debian/changelog opendmarc-1.4.0~beta1+dfsg/debian/changelog
> --- opendmarc-1.4.0~beta1+dfsg/debian/changelog	2020-09-19 08:40:47.000000000 +0200
> +++ opendmarc-1.4.0~beta1+dfsg/debian/changelog	2021-05-29 16:22:50.000000000 +0200
> @@ -1,3 +1,12 @@
> +opendmarc (1.4.0~beta1+dfsg-4) unstable; urgency=high
> +
> +  * Backport patches from upstream version 1.4.1.1 (Closes: #977766, #977767):
> +    - CVE-2019-16378: Fix handling of multi-valued From headers
> +    - CVE-2019-20790: Validate incoming SPF headers
> +    - CVE-2020-12272: Check DKIM and SPF domain syntax
> +
> + -- David Bürgin <dbuergin@gluet.ch>  Sat, 29 May 2021 16:22:50 +0200
> +
>  opendmarc (1.4.0~beta1+dfsg-3) unstable; urgency=high
>  
>    * Cherry-pick patch for CVE-2020-12460 from upstream:
> diff -Nru opendmarc-1.4.0~beta1+dfsg/debian/libopendmarc2.symbols opendmarc-1.4.0~beta1+dfsg/debian/libopendmarc2.symbols
> --- opendmarc-1.4.0~beta1+dfsg/debian/libopendmarc2.symbols	2020-06-16 20:41:13.000000000 +0200
> +++ opendmarc-1.4.0~beta1+dfsg/debian/libopendmarc2.symbols	2021-05-29 15:03:47.000000000 +0200
> @@ -1,5 +1,6 @@
>  libopendmarc.so.2 libopendmarc2 #MINVER#
>  * Build-Depends-Package: libopendmarc-dev
> + check_domain@Base 1.4.0~beta1+dfsg

If check_domain is really supposed to be part of the public ABI, then
this should by versioned as 1.4.0~beta1+dfsg-4~ as it is introduced in a
patch. Since check_domain is an overly generic name and it is not
exposed in any header, I suspect that it is not intended to be public.
In that case, sticking a static to the definition of check_domain will
remove the symbol from the public ABI. It's only used inside
libopendmarc/opendmarc_policy.c anyway.

Please remove it from the public ABI or fix the version in the symbols
file.

Cheers

>   dmarc_dns_get_record@Base 1.1.0~beta2
>   dmarc_strlcat@Base 1.1.0~beta2
>   dmarc_strlcpy@Base 1.1.0~beta2
> diff -Nru opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-16378.patch opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-16378.patch
> --- opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-16378.patch	1970-01-01 01:00:00.000000000 +0100
> +++ opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-16378.patch	2021-05-29 16:22:50.000000000 +0200
> @@ -0,0 +1,321 @@
> +Description: CVE-2019-16378: Handle multi-valued From header, add RejectMultiValueFrom parameter
> +Author: Murray S. Kucherawy <msk@trusteddomain.org>
> +Origin: backport, https://github.com/trusteddomainproject/OpenDMARC/releases/tag/rel-opendmarc-1-4-1-1
> +
> +--- a/opendmarc/parse.c
> ++++ b/opendmarc/parse.c
> +@@ -12,10 +12,18 @@
> + #include <string.h>
> + #include <limits.h>
> + #include <stdio.h>
> ++#include <stdlib.h>
> + 
> + /* opendmarc includes */
> + #include "util.h"
> + 
> ++#ifndef FALSE
> ++# define FALSE	0
> ++#endif /* ! FALSE */
> ++#ifndef TRUE
> ++# define TRUE	1
> ++#endif /* ! TRUE */
> ++
> + /* types */
> + typedef unsigned long cmap_elem_type;
> + 
> +@@ -24,6 +32,7 @@
> + #define MAILPARSE_ERR_PUNBALANCED	1	/* unbalanced parentheses */
> + #define MAILPARSE_ERR_QUNBALANCED	2	/* unbalanced quotes */
> + #define MAILPARSE_ERR_SUNBALANCED	3	/* unbalanced sq. brackets */
> ++#define MAILPARSE_ERR_MULTIVALUE	4	/* multiple possible values */
> + 
> + /* a bitmap for the "specials" character class */
> + #define	CMAP_NBITS	 	(sizeof(cmap_elem_type) * CHAR_BIT)
> +@@ -466,6 +475,160 @@
> + 	}
> + }
> + 
> ++/*
> ++**  DMARCF_MAIL_PARSE_MULTI -- extract the local-part and hostname from a mail
> ++**                             header field that might contain multiple
> ++**                             values, e.g. "To:", "Cc:"
> ++**
> ++**  Parameters:
> ++**  	line -- input line
> ++**  	users_out -- array of pointers to "local-part" (returned)
> ++**  	domains_out -- array of pointers to hostname (returned)
> ++**
> ++**  Return value:
> ++**  	0 on success, or an DKIM_MAILPARSE_ERR_* on failure.
> ++**
> ++**  Notes:
> ++**  	Input string is modified.
> ++*/
> ++
> ++int
> ++dmarcf_mail_parse_multi(unsigned char *line, unsigned char ***users_out,
> ++                        unsigned char ***domains_out)
> ++{
> ++	_Bool escaped = FALSE;
> ++	_Bool quoted = FALSE;
> ++	_Bool done = FALSE;
> ++	int a = 0;
> ++	int n = 0;
> ++	int status;
> ++	int parens = 0;
> ++	char *p;
> ++	char *addr;
> ++	unsigned char **uout = NULL;
> ++	unsigned char **dout = NULL;
> ++	unsigned char *u;
> ++	unsigned char *d;
> ++
> ++	/* walk the input string looking for unenclosed commas */
> ++	addr = line;
> ++	for (p = line; !done; p++)
> ++	{
> ++		if (escaped)
> ++		{
> ++			escaped = FALSE;
> ++			continue;
> ++		}
> ++
> ++		switch (*p)
> ++		{
> ++		  case '\\':
> ++			escaped = TRUE;
> ++			continue;
> ++
> ++		  case '"':
> ++			quoted = !quoted;
> ++			continue;
> ++
> ++		  case '(':
> ++			parens++;
> ++			continue;
> ++
> ++		  case ')':
> ++			parens--;
> ++			continue;
> ++
> ++		  case ',':
> ++			/* skip it if it's quoted or in a comment */
> ++			if (parens != 0 || quoted)
> ++				continue;
> ++			/* FALLTHROUGH */
> ++
> ++		  case '\0':
> ++			if (*p == '\0')
> ++				done = TRUE;
> ++			else
> ++				*p = '\0';
> ++
> ++			status = dmarcf_mail_parse(addr, &u, &d);
> ++			if (status != 0)
> ++			{
> ++				if (uout != NULL)
> ++				{
> ++					free(uout);
> ++					free(dout);
> ++				}
> ++
> ++				return status;
> ++			}
> ++
> ++			if (n == 0)
> ++			{
> ++				size_t newsize = 2 * sizeof(unsigned char *);
> ++
> ++				uout = (unsigned char **) malloc(newsize);
> ++				if (uout == NULL)
> ++					return -1;
> ++
> ++				dout = (unsigned char **) malloc(newsize);
> ++				if (dout == NULL)
> ++				{
> ++					free(uout);
> ++					return -1;
> ++				}
> ++
> ++				a = 2;
> ++			}
> ++			else if (n + 1 == a)
> ++			{
> ++				unsigned char **new;
> ++
> ++				size_t newsize = a * 2 * sizeof(unsigned char *);
> ++
> ++				new = (unsigned char **) realloc(uout, newsize);
> ++				if (new == NULL)
> ++				{
> ++					free(uout);
> ++					free(dout);
> ++					return -1;
> ++				}
> ++
> ++				uout = new;
> ++
> ++				new = (unsigned char **) realloc(dout, newsize);
> ++				if (new == NULL)
> ++				{
> ++					free(uout);
> ++					free(dout);
> ++					return -1;
> ++				}
> ++
> ++				dout = new;
> ++
> ++				a *= 2;
> ++			}
> ++
> ++			uout[n] = u;
> ++			dout[n++] = d;
> ++
> ++			uout[n] = (char *) NULL;
> ++			dout[n] = (char *) NULL;
> ++
> ++			addr = p + 1;
> ++
> ++			break;
> ++
> ++		  default:
> ++			break;
> ++		}
> ++	}
> ++
> ++	*users_out = uout;
> ++	*domains_out = dout;
> ++
> ++	return 0;
> ++}
> ++
> + #ifdef MAILPARSE_TEST
> + int
> + main(int argc, char **argv)
> +--- a/opendmarc/opendmarc.c
> ++++ b/opendmarc/opendmarc.c
> +@@ -177,6 +177,7 @@
> + 	_Bool			conf_spfselfvalidate;
> + #endif /* WITH_SPF */
> + 	_Bool			conf_ignoreauthclients;
> ++	_Bool			conf_reject_multi_from;
> + 	unsigned int		conf_refcnt;
> + 	unsigned int		conf_dnstimeout;
> + 	struct config *		conf_data;
> +@@ -1365,6 +1366,10 @@
> + 		                  &conf->conf_afrf,
> + 		                  sizeof conf->conf_afrf);
> + 
> ++		(void) config_get(data, "RejectMultiValueFrom",
> ++		                  &conf->conf_reject_multi_from,
> ++		                  sizeof conf->conf_reject_multi_from);
> ++
> + 		(void) config_get(data, "FailureReportsOnNone",
> + 		                  &conf->conf_afrfnone,
> + 		                  sizeof conf->conf_afrfnone);
> +@@ -2291,8 +2296,10 @@
> + 	struct dmarcf_header *from;
> + 	struct arcseal_header *as_hdr;
> + 	u_char *reqhdrs_error = NULL;
> +-	u_char *user;
> +-	u_char *domain;
> ++	u_char *user = NULL;
> ++	u_char **users;
> ++	u_char *domain = NULL;
> ++	u_char **domains;
> + 	u_char *bang;
> + 	u_char **ruv;
> + 	unsigned char header[MAXHEADER + 1];
> +@@ -2414,10 +2421,36 @@
> + 		return SMFIS_ACCEPT;
> + 	}
> + 
> +-	/* extract From: domain */
> ++	/* extract From: addresses */
> + 	memset(addrbuf, '\0', sizeof addrbuf);
> + 	strncpy(addrbuf, from->hdr_value, sizeof addrbuf - 1);
> +-	status = dmarcf_mail_parse(addrbuf, &user, &domain);
> ++	status = dmarcf_mail_parse_multi(addrbuf, &users, &domains);
> ++	if (status == 0 && (users[0] != NULL || domains[0] != NULL))
> ++	{
> ++		/*
> ++		**  Enact special handling for a multi-valued from if
> ++		**  the domains are not all the same.
> ++		*/
> ++
> ++		for (c = 1; users[c] != NULL; c++)
> ++		{
> ++			if (strcasecmp(domains[0], domains[c]) != 0)
> ++			{
> ++				syslog(LOG_ERR,
> ++				       "%s: multi-valued From field detected",
> ++				       dfc->mctx_jobid);
> ++			}
> ++
> ++			if (conf->conf_reject_multi_from)
> ++				return SMFIS_REJECT;
> ++			else
> ++				return SMFIS_ACCEPT;
> ++		}
> ++
> ++		user = users[0];
> ++		domain = domains[0];
> ++	}
> ++
> + 	if (status != 0 || user == NULL || domain == NULL)
> + 	{
> + 		if (conf->conf_dolog)
> +--- a/opendmarc/opendmarc-config.h
> ++++ b/opendmarc/opendmarc-config.h
> +@@ -46,6 +46,7 @@
> + 	{ "RecordAllMessages",		CONFIG_TYPE_BOOLEAN,	FALSE },
> + 	{ "RequiredHeaders",		CONFIG_TYPE_BOOLEAN,	FALSE },
> + 	{ "RejectFailures",		CONFIG_TYPE_BOOLEAN,	FALSE },
> ++	{ "RejectMultiValueFrom",	CONFIG_TYPE_BOOLEAN,	FALSE },
> +         { "RejectString",		CONFIG_TYPE_STRING,	FALSE },
> + 	{ "ReportCommand",		CONFIG_TYPE_STRING,	FALSE },
> + 	{ "Socket",			CONFIG_TYPE_STRING,	FALSE },
> +--- a/opendmarc/opendmarc.conf.5.in
> ++++ b/opendmarc/opendmarc.conf.5.in
> +@@ -251,6 +251,12 @@
> + The default is "false".
> + 
> + .TP
> ++.I RejectMultiValueFrom (Boolean)
> ++If set, messages with multiple addresses in the From: field of the message
> ++will be rejected unless all domain names in that field are the same.  They
> ++will otherwise be ignored by the filter (the default).
> ++
> ++.TP
> + .I RejectString (string)
> + This string describes the reason of reject at SMTP level.
> + The message MUST contain the word "%s" once, which will be replaced by
> +--- a/opendmarc/opendmarc.conf.sample
> ++++ b/opendmarc/opendmarc.conf.sample
> +@@ -295,6 +295,15 @@
> + #
> + # RejectFailures false
> + 
> ++##  RejectMultiValueFrom { true | false }
> ++##  	default "false"
> ++##
> ++##  If set, messages with multiple addresses in the From: field of the message
> ++##  will be rejected unless all domains in the field are the same.  They will
> ++##  otherwise be ignored by the filter (the default).
> ++#
> ++# RejectMultiValueFrom false
> ++
> + ## RejectString string
> + ##	default ("rejected by DMARC policy for %s")
> + ##
> +--- a/opendmarc/parse.h
> ++++ b/opendmarc/parse.h
> +@@ -22,5 +22,8 @@
> + /* prototypes */
> + extern int dmarcf_mail_parse __P((unsigned char *, unsigned char **,
> +                                   unsigned char **));
> ++extern int dmarcf_mail_parse_multi __P((char *, unsigned char ***,
> ++                                       unsigned char ***));
> ++
> + 
> + #endif /* ! _DMARCF_MAILPARSE_H_ */
> diff -Nru opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-20790.patch opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-20790.patch
> --- opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-20790.patch	1970-01-01 01:00:00.000000000 +0100
> +++ opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2019-20790.patch	2021-05-29 16:22:50.000000000 +0200
> @@ -0,0 +1,300 @@
> +Description: CVE-2019-20790: Properly validate incoming headers that carry SPF results
> +Author: Murray S. Kucherawy <msk@trusteddomain.org>
> +Origin: backport, https://github.com/trusteddomainproject/OpenDMARC/releases/tag/rel-opendmarc-1-4-1-1
> +
> +--- a/opendmarc/opendmarc.c
> ++++ b/opendmarc/opendmarc.c
> +@@ -435,25 +435,46 @@
> + **
> + **  Parameters:
> + **  	str -- the value of the Received-SPF field to analyze
> ++**  	envdomain -- envelope sender domain against which to test
> + **
> + **  Return value:
> + **  	A ARES_RESULT_* constant.
> ++**
> ++**  Notes:
> ++**  	We will not accept a result delivered via a discovered Received-SPF
> ++**  	header field unless (a) it includes the "identity" key and its
> ++**  	value is "mailfrom", AND (b) it includes the "envelope-from" key and
> ++**  	its value matches the envelope sender we got via milter.  If either
> ++**  	of those tests fails, a "pass" or a "fail" is interpreted as "neutral".
> ++**  	This is necessary to be compliant with RFC 7489 Section 4.1,
> ++**  	which says the SPF evaluation of MAIL FROM is what DMARC consumes.
> + */
> + 
> + int
> +-dmarcf_parse_received_spf(char *str)
> ++dmarcf_parse_received_spf(char *str, char *envdomain)
> + {
> +-	_Bool copying = FALSE;
> ++	_Bool in_result = TRUE;
> + 	_Bool escaped = FALSE;
> ++	_Bool quoting = FALSE;
> + 	int parens = 0;
> + 	char *p;
> + 	char *r;
> + 	char *end;
> + 	char result[MAXSPFRESULT + 1];
> ++	char spf_envdomain[BUFRSZ + 1];
> ++	char key[BUFRSZ + 1];
> ++	char value[BUFRSZ + 1];
> ++	char identity[BUFRSZ + 1];
> + 
> + 	assert(str != NULL);
> + 
> ++	memset(spf_envdomain, '\0', sizeof spf_envdomain);
> ++	memset(key, '\0', sizeof key);
> ++	memset(value, '\0', sizeof value);
> ++	memset(identity, '\0', sizeof identity);
> + 	memset(result, '\0', sizeof result);
> ++
> ++	/* first thing we get is the result token */
> + 	r = result;
> + 	end = &result[sizeof result - 1];
> + 
> +@@ -461,33 +482,13 @@
> + 	{
> + 		if (escaped)
> + 		{
> +-			if (copying)
> +-			{
> +-				if (r < end)
> +-					*r++ = *p;
> +-			}
> +-
> ++			if (parens == 0 && r < end)
> ++				*r++ = *p;
> + 			escaped = FALSE;
> + 		}
> +-		else if (copying)
> ++		else if (*p == '\\')
> + 		{
> +-			if (!escaped && *p == '\\')
> +-			{
> +-				escaped = TRUE;
> +-			}
> +-			else if (*p == '(')
> +-			{
> +-				copying = FALSE;
> +-				parens++;
> +-			}
> +- 			else if (isascii(*p) && isspace(*p))
> +-			{
> +-				copying = FALSE;
> +-			}
> +-			else if (r < end)
> +-			{
> +-				*r++ = *p;
> +-			}
> ++			escaped = TRUE;
> + 		}
> + 		else if (*p == '(')
> + 		{
> +@@ -499,35 +500,117 @@
> + 		}
> + 		else if (parens == 0)
> + 		{
> ++			if (*p == '"')
> ++			{
> ++				/* entering/leaving a quoted substring */
> ++				quoting = !quoting;
> ++				continue;
> ++			}
> ++
> ++			/* a possibly meaningful character */
> + 			if (isascii(*p) && isspace(*p))
> ++			{
> ++				/* a space while quoting; just continue */
> ++				if (quoting)
> ++					continue;
> ++
> ++				if (in_result)
> ++				{
> ++					in_result = FALSE;
> ++					r = key;
> ++					end = &key[sizeof key - 1];
> ++				}
> + 				continue;
> ++			}
> + 
> +-			if (!copying)
> ++			if (!in_result && *p == '=')
> + 			{
> +-				if (result[0] != '\0')
> +-					break;
> ++				r = value;
> ++				end = &value[sizeof value - 1];
> ++			}
> ++			else if (!in_result && *p == ';')
> ++			{
> ++				if (strcasecmp(key, "identity") == 0)
> ++				{
> ++					strlcpy(identity, value,
> ++					        sizeof identity);
> ++				}
> ++
> ++				if (strcasecmp(key, "envelope-from") == 0)
> ++				{
> ++					strlcpy(spf_envdomain, value,
> ++					        sizeof spf_envdomain);
> ++				}
> ++
> ++				memset(key, '\0', sizeof key);
> ++				memset(value, '\0', sizeof value);
> + 
> +-				copying = TRUE;
> +-				if (r < end)
> +-					*r++ = *p;
> ++				r = key;
> ++				end = &key[sizeof key - 1];
> ++			}
> ++			else if (r < end)
> ++			{
> ++				*r++ = *p;
> + 			}
> + 		}
> + 	}
> + 
> +-	if (strcasecmp(result, "pass") == 0)
> ++	if (key[0] != '\0')
> ++	{
> ++		if (strcasecmp(key, "identity") == 0)
> ++			strlcpy(identity, value, sizeof identity);
> ++		if (strcasecmp(key, "envelope-from") == 0)
> ++			strlcpy(spf_envdomain, value, sizeof spf_envdomain);
> ++	}
> ++
> ++	p = strchr(spf_envdomain, '@');
> ++	if (p != NULL)
> ++	{
> ++		r = spf_envdomain;
> ++		p = p + 1;
> ++		for (;;)
> ++		{
> ++			*r = *p;
> ++			if (*p == '\0')
> ++				break;
> ++			r++;
> ++			p++;
> ++		}
> ++	}
> ++
> ++	if (strcasecmp(identity, "mailfrom") != 0 ||
> ++            strcasecmp(spf_envdomain, envdomain) != 0)
> ++	{
> ++		return ARES_RESULT_NEUTRAL;
> ++	}
> ++	else if (strcasecmp(result, "pass") == 0)
> ++	{
> + 		return ARES_RESULT_PASS;
> ++	}
> + 	else if (strcasecmp(result, "fail") == 0)
> ++	{
> + 		return ARES_RESULT_FAIL;
> ++	}
> + 	else if (strcasecmp(result, "softfail") == 0)
> ++	{
> + 		return ARES_RESULT_SOFTFAIL;
> ++	}
> + 	else if (strcasecmp(result, "neutral") == 0)
> ++	{
> + 		return ARES_RESULT_NEUTRAL;
> ++	}
> + 	else if (strcasecmp(result, "temperror") == 0)
> ++	{
> + 		return ARES_RESULT_TEMPERROR;
> ++	}
> + 	else if (strcasecmp(result, "none") == 0)
> ++	{
> + 		return ARES_RESULT_NONE;
> ++	}
> + 	else
> ++	{
> + 		return ARES_RESULT_PERMERROR;
> ++	}
> + }
> + 
> + /*
> +@@ -2678,13 +2761,47 @@
> + #endif
> + 			)
> + 			{
> ++				_Bool envfrom_match = FALSE;
> + 				int spfmode;
> ++				int i;
> + 
> + 				dfc->mctx_spfresult = ar.ares_result[c].result_result;
> + 
> + 				if (ar.ares_result[c].result_result != ARES_RESULT_PASS)
> + 					continue;
> + 
> ++				/*
> ++				**  Confirm the method used was "smtp.mailfrom"
> ++				**  and it matches our envelope sender.
> ++				*/
> ++
> ++				for (i = 0;
> ++				     i < ar.ares_result[c].result_props;
> ++				     i++)
> ++				{
> ++					if (ar.ares_result[c].result_ptype[i] == ARES_PTYPE_SMTP &&
> ++					    strcasecmp(ar.ares_result[c].result_property[i],
> ++					               "mailfrom") == 0)
> ++					{
> ++						char *d;
> ++
> ++						d = strchr(ar.ares_result[c].result_value[i],
> ++						           '@');
> ++						if (d == NULL)
> ++							d = ar.ares_result[c].result_value[i];
> ++
> ++						if (strcasecmp(d,
> ++						               dfc->mctx_envdomain) == 0)
> ++						{
> ++							envfrom_match = TRUE;
> ++							break;
> ++						}
> ++					}
> ++				}
> ++
> ++				if (!envfrom_match)
> ++					continue;
> ++
> + 				spfaddr = NULL;
> + 				spfmode = DMARC_POLICY_SPF_ORIGIN_HELO;
> + 
> +@@ -2928,7 +3045,8 @@
> + 				else
> + 					spfmode = DMARC_POLICY_SPF_ORIGIN_MAILFROM;
> + 
> +-				spfres = dmarcf_parse_received_spf(hdr->hdr_value);
> ++				spfres = dmarcf_parse_received_spf(hdr->hdr_value,
> ++				                                   dfc->mctx_envdomain);
> + 
> + 				dmarcf_dstring_printf(dfc->mctx_histbuf,
> + 				                      "spf %d\n", spfres);
> +@@ -3002,7 +3120,7 @@
> + 				&used_mfrom);
> + 			if (used_mfrom == TRUE)
> + 			{
> +-				use_domain = dfc->mctx_envfrom;
> ++				use_domain = dfc->mctx_envdomain;
> + 				spf_mode   = DMARC_POLICY_SPF_ORIGIN_MAILFROM;
> + 			}
> + 			else
> +@@ -3011,10 +3129,10 @@
> + 				spf_mode   = DMARC_POLICY_SPF_ORIGIN_HELO;
> + 			}
> + 			ostatus = opendmarc_policy_store_spf(cc->cctx_dmarc, 
> +-				                                     use_domain,
> +-				                                     spf_result,
> +-				                                     spf_mode,
> +-				                                     human);
> ++				                             use_domain,
> ++				                             spf_result,
> ++				                             spf_mode,
> ++				                             human);
> + 			switch (spf_result)
> + 			{
> + 			    case DMARC_POLICY_SPF_OUTCOME_PASS:
> diff -Nru opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2020-12272.patch opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2020-12272.patch
> --- opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2020-12272.patch	1970-01-01 01:00:00.000000000 +0100
> +++ opendmarc-1.4.0~beta1+dfsg/debian/patches/cve-2020-12272.patch	2021-05-29 16:22:50.000000000 +0200
> @@ -0,0 +1,67 @@
> +Description: CVE-2020-12272: Check syntax of DKIM and SPF domain names
> +Author: Murray S. Kucherawy <msk@trusteddomain.org>
> +Origin: backport, https://github.com/trusteddomainproject/OpenDMARC/releases/tag/rel-opendmarc-1-4-1-1
> +
> +--- a/libopendmarc/opendmarc_policy.c
> ++++ b/libopendmarc/opendmarc_policy.c
> +@@ -4,6 +4,8 @@
> + **  Copyright (c) 2012-2016, 2018, The Trusted Domain Project.  All rights reserved.
> + **************************************************************************/
> + 
> ++#include <ctype.h>
> ++
> + #include "opendmarc_internal.h"
> + #include "dmarc.h"
> + 
> +@@ -22,6 +24,33 @@
> + # include <opendmarc_strl.h>
> + #endif /* USE_DMARCSTRL_H */
> + 
> ++/*
> ++**  CHECK_DOMAIN -- check for syntactical validity of a domain name
> ++**
> ++**  Parameters:
> ++**  	domain -- domain name to check
> ++**
> ++**  Return value:
> ++**  	TRUE if the syntax was fine, FALSE otherwise.
> ++*/
> ++
> ++bool check_domain(u_char *domain)
> ++{
> ++	u_char *dp;
> ++
> ++	for (dp = domain; *dp != '\0'; dp++)
> ++	{
> ++		if (!(isalpha(*dp) ||
> ++		      isdigit(*dp) ||
> ++		      *dp == '.' ||
> ++		      *dp == '-' ||
> ++		      *dp == '_'))
> ++			return FALSE;
> ++	}
> ++
> ++	return TRUE;
> ++}
> ++
> + /**************************************************************************
> + ** OPENDMARC_POLICY_LIBRARY_INIT -- Initialize The Library
> + **	Parameters:
> +@@ -388,6 +417,8 @@
> + 	dp = opendmarc_util_finddomain(domain, domain_buf, sizeof domain_buf);
> + 	if (dp == NULL)
> + 		return DMARC_PARSE_ERROR_NO_DOMAIN;
> ++	if (!check_domain(dp))
> ++		return DMARC_PARSE_ERROR_BAD_VALUE;
> + 	if (human_readable != NULL)
> + 		pctx->spf_human_outcome = strdup((char *)human_readable);
> + 	pctx->spf_domain = strdup((char *)dp);
> +@@ -454,6 +485,8 @@
> + 		return DMARC_PARSE_ERROR_NULL_CTX;
> + 	if (d_equal_domain == NULL || strlen((char *)d_equal_domain) == 0)
> + 		return DMARC_PARSE_ERROR_EMPTY;
> ++	if (!check_domain(d_equal_domain))
> ++		return DMARC_PARSE_ERROR_BAD_VALUE;
> + 
> + 	switch (dkim_result)
> + 	{
> diff -Nru opendmarc-1.4.0~beta1+dfsg/debian/patches/series opendmarc-1.4.0~beta1+dfsg/debian/patches/series
> --- opendmarc-1.4.0~beta1+dfsg/debian/patches/series	2020-09-19 08:34:33.000000000 +0200
> +++ opendmarc-1.4.0~beta1+dfsg/debian/patches/series	2021-05-29 15:51:16.000000000 +0200
> @@ -9,3 +9,6 @@
>  ticket227.patch
>  pull48.patch
>  cve-2020-12460.patch
> +cve-2019-16378.patch
> +cve-2020-12272.patch
> +cve-2019-20790.patch


-- 
Sebastian Ramacher


Reply to: