diff --git a/CVE-2020-14154-1.patch b/CVE-2020-14154-1.patch new file mode 100644 index 0000000..1b818a1 --- /dev/null +++ b/CVE-2020-14154-1.patch @@ -0,0 +1,188 @@ +From bb0e6277a45a5d4c3a30d3b968eeb31d78124e95 Mon Sep 17 00:00:00 2001 +From: Kevin McCarthy +Date: Fri, 5 Jun 2020 15:21:03 -0700 +Subject: [PATCH] Fix GnuTLS tls_verify_peers() checking. + +* Change the function to pass the certstatus parameter by reference, +and indicate success/failure of the function via the return value. It +was previously returning the certstatus, but was also returning 0 or +the *unset* certstatus on error too. Since a 0 certstatus means +"success", this meant a gnutls_certificate_verify_peers2() failure +would be regarded as a valid cert. + +* The gnutls_certificate_type_get() inside tls_verify_peers() checks +the *client* certificate type. Since it was only called if gnutls_certificate_verify_peers2() failed, I assume was either a +mistake, or perhaps an attempt to give a special error message if the +client cert was OpenPGP. In either case, the error message was not +very informative, so just remove the call and special error message. + +* Fix GNUTLS_E_NO_CERTIFICATE_FOUND check to be against verify_ret +instead of certstat. + +* Fix gnutls_strerror() call to use verify_ret instead of certstat. + +* gnutls_certificate_verify_peers2() already calls and checks gnutls_auth_get_type(), so remove call at the beginning of tls_check_certificate(). + +* gnutls_certificate_verify_peers2() also verifies the certificate +type for the *server* is GNUTLS_CRT_X509. Add a comment about that. +--- + mutt_ssl_gnutls.c | 100 +++++++++++++++++++++++++++------------------- + 1 file changed, 60 insertions(+), 40 deletions(-) + +diff --git a/mutt_ssl_gnutls.c b/mutt_ssl_gnutls.c +index 8fc6421..19d47b3 100644 +--- a/mutt_ssl_gnutls.c ++++ b/mutt_ssl_gnutls.c +@@ -684,6 +684,9 @@ static int tls_check_stored_hostname (const gnutls_datum_t *cert, + return 0; + } + ++/* Returns 0 on success ++ * -1 on failure ++ */ + static int tls_check_preauth (const gnutls_datum_t *certdata, + gnutls_certificate_status_t certstat, + const char *hostname, int chainidx, int* certerr, +@@ -802,8 +805,8 @@ static int tls_check_preauth (const gnutls_datum_t *certdata, + return -1; + } + +-/* +- * Returns 0 on failure, nonzero on success. ++/* Returns 1 on success. ++ * 0 on failure. + */ + static int tls_check_one_certificate (const gnutls_datum_t *certdata, + gnutls_certificate_status_t certstat, +@@ -1086,44 +1089,57 @@ static int tls_check_one_certificate (const gnutls_datum_t *certdata, + mutt_menuDestroy (&menu); + gnutls_x509_crt_deinit (cert); + +- return (done == 2); ++ return (done == 2) ? 1 : 0; + } + +-/* sanity-checking wrapper for gnutls_certificate_verify_peers */ +-static gnutls_certificate_status_t tls_verify_peers (gnutls_session_t tlsstate) ++/* sanity-checking wrapper for gnutls_certificate_verify_peers. ++ * ++ * certstat is technically a bitwise-or of gnutls_certificate_status_t ++ * values. ++ * ++ * Returns: ++ * - 0 if certstat was set. note: this does not mean success. ++ * - nonzero on failure. ++ */ ++static int tls_verify_peers (gnutls_session_t tlsstate, ++ gnutls_certificate_status_t *certstat) + { + int verify_ret; +- unsigned int status; + +- verify_ret = gnutls_certificate_verify_peers2 (tlsstate, &status); ++ /* gnutls_certificate_verify_peers2() chains to ++ * gnutls_x509_trust_list_verify_crt2(). That function's documentation says: ++ * ++ * When a certificate chain of cert_list_size with more than one ++ * certificates is provided, the verification status will apply to ++ * the first certificate in the chain that failed ++ * verification. The verification process starts from the end of ++ * the chain (from CA to end certificate). The first certificate ++ * in the chain must be the end-certificate while the rest of the ++ * members may be sorted or not. ++ * ++ * This is why tls_check_certificate() loops from CA to host in that order, ++ * calling the menu, and recalling tls_verify_peers() for each approved ++ * cert in the chain. ++ */ ++ verify_ret = gnutls_certificate_verify_peers2 (tlsstate, certstat); ++ ++ /* certstat was set */ + if (!verify_ret) +- return status; ++ return 0; + +- if (status == GNUTLS_E_NO_CERTIFICATE_FOUND) +- { ++ if (verify_ret == GNUTLS_E_NO_CERTIFICATE_FOUND) + mutt_error (_("Unable to get certificate from peer")); +- mutt_sleep (2); +- return 0; +- } +- if (verify_ret < 0) +- { ++ else + mutt_error (_("Certificate verification error (%s)"), +- gnutls_strerror (status)); +- mutt_sleep (2); +- return 0; +- } +- +- /* We only support X.509 certificates (not OpenPGP) at the moment */ +- if (gnutls_certificate_type_get (tlsstate) != GNUTLS_CRT_X509) +- { +- mutt_error (_("Certificate is not X.509")); +- mutt_sleep (2); +- return 0; +- } ++ gnutls_strerror (verify_ret)); + +- return status; ++ mutt_sleep (2); ++ return verify_ret; + } + ++/* Returns 1 on success. ++ * 0 on failure. ++ */ + static int tls_check_certificate (CONNECTION* conn) + { + tlssockdata *data = conn->sockdata; +@@ -1133,15 +1149,16 @@ static int tls_check_certificate (CONNECTION* conn) + gnutls_certificate_status_t certstat; + int certerr, i, preauthrc, savedcert, rc = 0; + int rcpeer = -1; /* the result of tls_check_preauth() on the peer's EE cert */ ++ int rcsettrust; + +- if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) +- { +- mutt_error (_("Unable to get certificate from peer")); +- mutt_sleep (2); ++ /* tls_verify_peers() calls gnutls_certificate_verify_peers2(), ++ * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE ++ * and that get_certificate_type() for the server is GNUTLS_CRT_X509. ++ * If it returns 0, certstat will be set with failure codes for the first ++ * cert in the chain (from CA to host) with an error. ++ */ ++ if (tls_verify_peers (state, &certstat) != 0) + return 0; +- } +- +- certstat = tls_verify_peers (state); + + cert_list = gnutls_certificate_get_peers (state, &cert_list_size); + if (!cert_list) +@@ -1184,12 +1201,15 @@ static int tls_check_certificate (CONNECTION* conn) + + /* add signers to trust set, then reverify */ + if (i && rc) { +- rc = gnutls_certificate_set_x509_trust_mem (data->xcred, &cert_list[i], +- GNUTLS_X509_FMT_DER); +- if (rc != 1) +- dprint (1, (debugfile, "error trusting certificate %d: %d\n", i, rc)); ++ rcsettrust = gnutls_certificate_set_x509_trust_mem (data->xcred, ++ &cert_list[i], ++ GNUTLS_X509_FMT_DER); ++ if (rcsettrust != 1) ++ dprint (1, (debugfile, "error trusting certificate %d: %d\n", i, rcsettrust)); ++ ++ if (tls_verify_peers (state, &certstat) != 0) ++ return 0; + +- certstat = tls_verify_peers (state); + /* If the cert chain now verifies, and the peer's cert was otherwise + * valid (rcpeer==0), we are done. + */ +-- +2.27.0 + diff --git a/CVE-2020-14154-2.patch b/CVE-2020-14154-2.patch new file mode 100644 index 0000000..bcbc3d0 --- /dev/null +++ b/CVE-2020-14154-2.patch @@ -0,0 +1,38 @@ +From 5fccf603ebcf352ba783136d6b2d2600d811fb3b Mon Sep 17 00:00:00 2001 +From: Kevin McCarthy +Date: Fri, 5 Jun 2020 18:16:31 -0700 +Subject: [PATCH] Abort GnuTLS certificate check if a cert in the chain is + rejected. + +GnuTLS is not checking dates because we disabled that in +tls_negotiate(). + +So if we don't do this, rejecting an expired intermediate cert will +have no effect. Certstat won't contain an expiration error, and +tls_check_preauth() will only look at each subsequent cert in the +chain's dates. +--- + mutt_ssl_gnutls.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/mutt_ssl_gnutls.c b/mutt_ssl_gnutls.c +index 19d47b3..6f98f50 100644 +--- a/mutt_ssl_gnutls.c ++++ b/mutt_ssl_gnutls.c +@@ -1199,8 +1199,12 @@ static int tls_check_certificate (CONNECTION* conn) + rc = tls_check_one_certificate (&cert_list[i], certstat, conn->account.host, + i, cert_list_size); + ++ /* Stop checking if the menu cert is aborted or rejected. */ ++ if (!rc) ++ break; ++ + /* add signers to trust set, then reverify */ +- if (i && rc) { ++ if (i) { + rcsettrust = gnutls_certificate_set_x509_trust_mem (data->xcred, + &cert_list[i], + GNUTLS_X509_FMT_DER); +-- +2.27.0 + diff --git a/CVE-2020-14154-3.patch b/CVE-2020-14154-3.patch new file mode 100644 index 0000000..8111537 --- /dev/null +++ b/CVE-2020-14154-3.patch @@ -0,0 +1,67 @@ +From f64ec1deefb67d471a642004e102cd1c501a1db3 Mon Sep 17 00:00:00 2001 +From: Kevin McCarthy +Date: Sat, 6 Jun 2020 20:03:56 -0700 +Subject: [PATCH] Fix GnuTLS interactive prompt short-circuiting. + +tls_verify_peers() doesn't verify expiration dates. So aborting early +because of a 0 certstat and the leaf passing tls_check_preauth() does +not mean subsequent intermediate certs are okay: they could beexpired. + +In the saved-cert preauth loop, instead of just noting the +tls_check_preauth() rc for the leaf, note the highest cert that passes +preauth. + +Then, in the interactive loop (which goes in the opposite order, from +CA to leaf) check that value instead. Since we are trusting certs one +by one, anything that passed in the previous loop will certainly pass +the preauth check at the beginning of tls_check_one_certificate(). +--- + mutt_ssl_gnutls.c | 17 ++++++----------- + 1 file changed, 6 insertions(+), 11 deletions(-) + +diff --git a/mutt_ssl_gnutls.c b/mutt_ssl_gnutls.c +index 6f98f50..09d628a 100644 +--- a/mutt_ssl_gnutls.c ++++ b/mutt_ssl_gnutls.c +@@ -1148,7 +1148,7 @@ static int tls_check_certificate (CONNECTION* conn) + unsigned int cert_list_size = 0; + gnutls_certificate_status_t certstat; + int certerr, i, preauthrc, savedcert, rc = 0; +- int rcpeer = -1; /* the result of tls_check_preauth() on the peer's EE cert */ ++ int max_preauth_pass = -1; + int rcsettrust; + + /* tls_verify_peers() calls gnutls_certificate_verify_peers2(), +@@ -1176,13 +1176,8 @@ static int tls_check_certificate (CONNECTION* conn) + rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i, + &certerr, &savedcert); + preauthrc += rc; +- if (i == 0) +- { +- /* This is the peer's end-entity X.509 certificate. Stash the result +- * to check later in this function. +- */ +- rcpeer = rc; +- } ++ if (!preauthrc) ++ max_preauth_pass = i; + + if (savedcert) + { +@@ -1214,10 +1209,10 @@ static int tls_check_certificate (CONNECTION* conn) + if (tls_verify_peers (state, &certstat) != 0) + return 0; + +- /* If the cert chain now verifies, and the peer's cert was otherwise +- * valid (rcpeer==0), we are done. ++ /* If the cert chain now verifies, and all lower certs already ++ * passed preauth, we are done. + */ +- if (!certstat && !rcpeer) ++ if (!certstat && (max_preauth_pass >= i - 1)) + return 1; + } + } +-- +2.27.0 + diff --git a/mutt.spec b/mutt.spec index 19f9b16..0be93c7 100644 --- a/mutt.spec +++ b/mutt.spec @@ -1,6 +1,6 @@ Name: mutt Version: 1.10.1 -Release: 6 +Release: 7 Epoch: 5 Summary: Text-based mail client License: GPLv2+ and Public Domain @@ -19,6 +19,9 @@ Patch13: CVE-2020-28896.patch Patch14: CVE-2021-3181.patch Patch15: backport-CVE-2020-14093-1.patch Patch16: backport-CVE-2020-14093-2.patch +Patch17: CVE-2020-14154-1.patch +Patch18: CVE-2020-14154-2.patch +Patch19: CVE-2020-14154-3.patch BuildRequires: gcc ncurses-devel gettext automake /usr/bin/xsltproc BuildRequires: lynx docbook-style-xsl perl-interpreter perl-generators @@ -123,6 +126,9 @@ ln -sf ./muttrc.5 %{buildroot}%{_mandir}/man5/muttrc.local.5 %{_mandir}/man5/muttrc.* %changelog +- Fri Oct 15 2021 yaoxin - 1.10.1-7 +- fix CVE-2020-14154 + - Tue Jul 27 2021 wangyue - 1.10.1-6 - fix CVE-2020-14093