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: