File mutt-1.10.1-backport-mutt_ssl_gnutls-1.14.3.dif of Package mutt.17615

---
 init.h            |    6 
 mutt.h            |    1 
 mutt_ssl.c        |   10 +
 mutt_ssl_gnutls.c |  342 ++++++++++++++++++++++++++++++------------------------
 4 files changed, 210 insertions(+), 149 deletions(-)

--- init.h
+++ init.h	2020-06-24 08:38:09.498048378 +0000
@@ -3578,6 +3578,12 @@ struct option_t MuttVars[] = {
   { "ssl_use_tlsv1_2", DT_BOOL, R_NONE, OPTTLSV1_2, 1 },
   /*
   ** .pp
+  ** If \fIset\fP , Mutt will use TLSv1.2 when communicating with servers that
+  ** request it.
+  */
+  { "ssl_use_tlsv1_3", DT_BOOL, R_NONE, OPTTLSV1_3, 1 },
+  /*
+  ** .pp
   ** This variable specifies whether to attempt to use TLSv1.2 in the
   ** SSL authentication process.
   */
--- mutt.h
+++ mutt.h	2020-06-24 08:38:09.498048378 +0000
@@ -416,6 +416,7 @@ enum
   OPTTLSV1,
   OPTTLSV1_1,
   OPTTLSV1_2,
+  OPTTLSV1_3,
   OPTSSLFORCETLS,
   OPTSSLVERIFYDATES,
   OPTSSLVERIFYHOST,
--- mutt_ssl.c
+++ mutt_ssl.c	2020-06-24 08:38:09.498048378 +0000
@@ -196,6 +196,10 @@ int mutt_ssl_starttls (CONNECTION* conn)
     dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL_CTX\n"));
     goto bail_ssldata;
   }
+#ifdef SSL_OP_NO_TLSv1_3
+  if (!option(OPTTLSV1_3))
+    ssl_options |= SSL_OP_NO_TLSv1_3;
+#endif
 #ifdef SSL_OP_NO_TLSv1_2
   if (!option(OPTTLSV1_2))
     ssl_options |= SSL_OP_NO_TLSv1_2;
@@ -469,6 +473,12 @@ static int ssl_socket_open (CONNECTION *
     SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_2);
   }
 #endif
+#ifdef SSL_OP_NO_TLSv1_3
+  if (!option(OPTTLSV1_3))
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1_3);
+  }
+#endif
   if (!option(OPTSSLV2))
   {
     SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
--- mutt_ssl_gnutls.c
+++ mutt_ssl_gnutls.c	2020-06-24 08:50:43.248014566 +0000
@@ -42,6 +42,7 @@
 #define CERTERR_HOSTNAME    16
 #define CERTERR_SIGNERNOTCA 32
 #define CERTERR_INSECUREALG 64
+#define CERTERR_OTHER       128
 
 /* deprecated types compatibility */
 
@@ -84,6 +85,7 @@ tlssockdata;
 /* local prototypes */
 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len);
 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len);
+static int tls_socket_poll (CONNECTION* conn, time_t wait_secs);
 static int tls_socket_open (CONNECTION* conn);
 static int tls_socket_close (CONNECTION* conn);
 static int tls_starttls_close (CONNECTION* conn);
@@ -122,7 +124,7 @@ int mutt_ssl_socket_setup (CONNECTION* c
   conn->conn_read	= tls_socket_read;
   conn->conn_write	= tls_socket_write;
   conn->conn_close	= tls_socket_close;
-  conn->conn_poll       = raw_socket_poll;
+  conn->conn_poll       = tls_socket_poll;
 
   return 0;
 }
@@ -141,15 +143,16 @@ static int tls_socket_read (CONNECTION*
 
   do {
     ret = gnutls_record_recv (data->state, buf, len);
-    if (ret < 0 && gnutls_error_is_fatal(ret) == 1)
-    {
-      mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
-      mutt_sleep (4);
-      return -1;
-    }
   }
   while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
 
+  if (ret < 0)
+  {
+    mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
+    mutt_sleep (4);
+    return -1;
+  }
+
   return ret;
 }
 
@@ -168,23 +171,37 @@ static int tls_socket_write (CONNECTION*
 
   do
   {
-    ret = gnutls_record_send (data->state, buf + sent, len - sent);
+    do
+    {
+      ret = gnutls_record_send (data->state, buf + sent, len - sent);
+    } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
     if (ret < 0)
     {
-      if (gnutls_error_is_fatal(ret) == 1)
-      {
-	mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
-	mutt_sleep (4);
-	return -1;
-      }
-      return ret;
+      mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
+      mutt_sleep (4);
+      return -1;
     }
+
     sent += ret;
   } while (sent < len);
 
   return sent;
 }
 
+static int tls_socket_poll (CONNECTION* conn, time_t wait_secs)
+{
+  tlssockdata *data = conn->sockdata;
+
+  if (!data)
+    return -1;
+
+  if (gnutls_record_check_pending (data->state))
+    return 1;
+  else
+    return raw_socket_poll (conn, wait_secs);
+}
+
 static int tls_socket_open (CONNECTION* conn)
 {
   if (raw_socket_open (conn) < 0)
@@ -210,6 +227,7 @@ int mutt_ssl_starttls (CONNECTION* conn)
   conn->conn_read	= tls_socket_read;
   conn->conn_write	= tls_socket_write;
   conn->conn_close	= tls_starttls_close;
+  conn->conn_poll       = tls_socket_poll;
 
   return 0;
 }
@@ -272,10 +290,10 @@ err_crt:
 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
 static int tls_set_priority(tlssockdata *data)
 {
-  size_t nproto = 4;
+  size_t nproto = 5;
   char *priority;
   size_t priority_size;
-  int err;
+  int err, rv = -1;
 
   priority_size = SHORT_STRING + mutt_strlen (SslCiphers);
   priority = safe_malloc (priority_size);
@@ -286,6 +304,11 @@ static int tls_set_priority(tlssockdata
   else
     safe_strcat (priority, priority_size, "NORMAL");
 
+  if (! option(OPTTLSV1_3))
+  {
+    nproto--;
+    safe_strcat (priority, priority_size, ":-VERS-TLS1.3");
+  }
   if (! option(OPTTLSV1_2))
   {
     nproto--;
@@ -301,7 +324,7 @@ static int tls_set_priority(tlssockdata
     nproto--;
     safe_strcat (priority, priority_size, ":-VERS-TLS1.0");
   }
-  if (! option(OPTSSLV3))
+  if (!option(OPTSSLV3))
   {
     nproto--;
     safe_strcat (priority, priority_size, ":-VERS-SSL3.0");
@@ -310,25 +333,30 @@ static int tls_set_priority(tlssockdata
   if (nproto == 0)
   {
     mutt_error (_("All available protocols for TLS/SSL connection disabled"));
-    FREE (&priority);
-    return -1;
+    goto cleanup;
   }
 
   if ((err = gnutls_priority_set_direct (data->state, priority, NULL)) < 0)
   {
     mutt_error ("gnutls_priority_set_direct(%s): %s", priority, gnutls_strerror(err));
     mutt_sleep (2);
-    FREE (&priority);
-    return -1;
+    goto cleanup;
   }
 
+  rv = 0;
+
+cleanup:
   FREE (&priority);
-  return 0;
+  return rv;
 }
 #else
 /* This array needs to be large enough to hold all the possible values support
  * by Mutt.  The initialized values are just placeholders--the array gets
  * overwrriten in tls_negotiate() depending on the $ssl_use_* options.
+ *
+ * Note: gnutls_protocol_set_priority() was removed in GnuTLS version
+ * 3.4 (2015-04).  TLS 1.3 support wasn't added until version 3.6.5.
+ * Therefore, no attempt is made to support $ssl_use_tlsv1_3 in this code.
  */
 static int protocol_priority[] = {GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3, 0};
 
@@ -445,7 +473,8 @@ static int tls_negotiate (CONNECTION * c
   {
     err = gnutls_handshake(data->state);
   }
-  if (err < 0) {
+  if (err < 0)
+  {
     if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
     {
       mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
@@ -479,7 +508,7 @@ static int tls_negotiate (CONNECTION * c
 
   return 0;
 
- fail:
+fail:
   gnutls_certificate_free_credentials (data->xcred);
   gnutls_deinit (data->state);
   FREE(&conn->sockdata);
@@ -517,6 +546,7 @@ static int tls_starttls_close (CONNECTIO
   conn->conn_read = raw_socket_read;
   conn->conn_write = raw_socket_write;
   conn->conn_close = raw_socket_close;
+  conn->conn_poll = raw_socket_poll;
 
   return rc;
 }
@@ -594,11 +624,11 @@ static int tls_compare_certificates (con
 static void tls_fingerprint (gnutls_digest_algorithm_t algo,
                              char* s, int l, const gnutls_datum_t* data)
 {
-  unsigned char md[36];
+  unsigned char md[64];
   size_t n;
   int j;
 
-  n = 36;
+  n = 64;
 
   if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
   {
@@ -681,6 +711,9 @@ static int tls_check_stored_hostname (co
   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,
@@ -706,6 +739,10 @@ static int tls_check_preauth (const gnut
     return -1;
   }
 
+  /* Note: tls_negotiate() contains a call to
+   * gnutls_certificate_set_verify_flags() with a flag disabling
+   * GnuTLS checking of the dates.  So certstat shouldn't have the
+   * GNUTLS_CERT_EXPIRED and GNUTLS_CERT_NOT_ACTIVATED bits set. */
   if (option (OPTSSLVERIFYDATES) != MUTT_NO)
   {
     if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
@@ -719,48 +756,27 @@ static int tls_check_preauth (const gnut
       && !tls_check_stored_hostname (certdata, hostname))
     *certerr |= CERTERR_HOSTNAME;
 
+  if (certstat & GNUTLS_CERT_REVOKED)
+  {
+    *certerr |= CERTERR_REVOKED;
+    certstat ^= GNUTLS_CERT_REVOKED;
+  }
+
   /* see whether certificate is in our cache (certificates file) */
   if (tls_compare_certificates (certdata))
   {
     *savedcert = 1;
 
-    if (chainidx == 0 && (certstat & GNUTLS_CERT_INVALID))
+    /* We check above for certs with bad dates or that are revoked.
+     * These must be accepted manually each time.  Otherwise, we
+     * accept saved certificates as valid. */
+    if (*certerr == CERTERR_VALID)
     {
-      /* doesn't matter - have decided is valid because server
-       certificate is in our trusted cache */
-      certstat ^= GNUTLS_CERT_INVALID;
-    }
-
-    if (chainidx == 0 && (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND))
-    {
-      /* doesn't matter that we haven't found the signer, since
-       certificate is in our trusted cache */
-      certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
-    }
-
-    if (chainidx <= 1 && (certstat & GNUTLS_CERT_SIGNER_NOT_CA))
-    {
-      /* Hmm. Not really sure how to handle this, but let's say
-       that we don't care if the CA certificate hasn't got the
-       correct X.509 basic constraints if server or first signer
-       certificate is in our cache. */
-      certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
-    }
-
-    if (chainidx == 0 && (certstat & GNUTLS_CERT_INSECURE_ALGORITHM))
-    {
-      /* doesn't matter that it was signed using an insecure
-         algorithm, since certificate is in our trusted cache */
-      certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
+      gnutls_x509_crt_deinit (cert);
+      return 0;
     }
   }
 
-  if (certstat & GNUTLS_CERT_REVOKED)
-  {
-    *certerr |= CERTERR_REVOKED;
-    certstat ^= GNUTLS_CERT_REVOKED;
-  }
-
   if (certstat & GNUTLS_CERT_INVALID)
   {
     *certerr |= CERTERR_NOTTRUSTED;
@@ -788,19 +804,22 @@ static int tls_check_preauth (const gnut
     certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
   }
 
+  /* we've been zeroing the interesting bits in certstat -
+   * don't return OK if there are any unhandled bits we don't
+   * understand */
+  if (certstat != 0)
+    *certerr |= CERTERR_OTHER;
+
   gnutls_x509_crt_deinit (cert);
 
-  /* we've been zeroing the interesting bits in certstat -
-   don't return OK if there are any unhandled bits we don't
-   understand */
-  if (*certerr == CERTERR_VALID && certstat == 0)
+  if (*certerr == CERTERR_VALID)
     return 0;
 
   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,
@@ -825,23 +844,12 @@ static int tls_check_one_certificate (co
   char title[STRING];
   FILE *fp;
   gnutls_datum_t pemdata;
-  int i, row, done, ret;
+  int i, row, done, ret, reset_ignoremacro = 0;
 
   if (!tls_check_preauth (certdata, certstat, hostname, idx, &certerr,
       &savedcert))
     return 1;
 
-  /* skip signers if insecure algorithm was used */
-  if (idx && (certerr & CERTERR_INSECUREALG))
-  {
-    if (idx == 1)
-    {
-      mutt_error (_("Warning: Server certificate was signed using an insecure algorithm"));
-      mutt_sleep (2);
-    }
-    return 0;
-  }
-
   /* interactive check from user */
   if (gnutls_x509_crt_init (&cert) < 0)
   {
@@ -958,8 +966,11 @@ static int tls_check_one_certificate (co
   tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), certdata);
   snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf);
   fpbuf[0] = '\0';
-  tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), certdata);
-  snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf);
+  fpbuf[40] = '\0';  /* Ensure the second printed line is null terminated */
+  tls_fingerprint (GNUTLS_DIG_SHA256, fpbuf, sizeof (fpbuf), certdata);
+  fpbuf[39] = '\0';  /* Divide into two lines of output */
+  snprintf (menu->dialog[row++], SHORT_STRING, _("SHA256 Fingerprint: %s"), fpbuf);
+  snprintf (menu->dialog[row++], SHORT_STRING, _("SHA256 Fingerprint: %s"), fpbuf + 40);
 
   if (certerr & CERTERR_NOTYETVALID)
   {
@@ -986,6 +997,12 @@ static int tls_check_one_certificate (co
     row++;
     strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING);
   }
+  if (certerr & CERTERR_INSECUREALG)
+  {
+    row++;
+    strfcpy (menu->dialog[row],
+                              _("Warning: Server certificate was signed using an insecure algorithm"), SHORT_STRING);
+  }
 
   snprintf (title, sizeof (title),
             _("SSL Certificate check (certificate %d of %d in chain)"),
@@ -994,27 +1011,27 @@ static int tls_check_one_certificate (co
   /* certificates with bad dates, or that are revoked, must be
    accepted manually each and every time */
   if (SslCertFile && !savedcert
-        && !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID
-                        | CERTERR_REVOKED)))
+      && !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID
+                      | CERTERR_REVOKED)))
   {
     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
-  /* L10N:
-   * These three letters correspond to the choices in the string:
-   * (r)eject, accept (o)nce, (a)ccept always.
-   * This is an interactive certificate confirmation prompt for
-   * a GNUTLS connection.
-   */
+    /* L10N:
+     * These three letters correspond to the choices in the string:
+     * (r)eject, accept (o)nce, (a)ccept always.
+     * This is an interactive certificate confirmation prompt for
+     * a GNUTLS connection.
+     */
     menu->keys = _("roa");
   }
   else
   {
     menu->prompt = _("(r)eject, accept (o)nce");
-  /* L10N:
-   * These two letters correspond to the choices in the string:
-   * (r)eject, accept (o)nce.
-   * These is an interactive certificate confirmation prompt for
-   * a GNUTLS connection.
-   */
+    /* L10N:
+     * These two letters correspond to the choices in the string:
+     * (r)eject, accept (o)nce.
+     * These is an interactive certificate confirmation prompt for
+     * a GNUTLS connection.
+     */
     menu->keys = _("ro");
   }
 
@@ -1026,7 +1043,11 @@ static int tls_check_one_certificate (co
   menu->help = helpstr;
 
   done = 0;
-  set_option (OPTIGNOREMACROEVENTS);
+  if (!option (OPTIGNOREMACROEVENTS))
+  {
+    set_option (OPTIGNOREMACROEVENTS);
+    reset_ignoremacro = 1;
+  }
   while (!done)
   {
     switch (mutt_menuLoop (menu))
@@ -1043,10 +1064,13 @@ static int tls_check_one_certificate (co
 	  /* save hostname if necessary */
 	  if (certerr & CERTERR_HOSTNAME)
 	  {
+            fpbuf[0] = '\0';
+            tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), certdata);
 	    fprintf(fp, "#H %s %s\n", hostname, fpbuf);
 	    done = 1;
 	  }
-	  if (certerr & CERTERR_NOTTRUSTED)
+          /* Save the cert for all other errors */
+	  if (certerr ^ CERTERR_HOSTNAME)
 	  {
             done = 0;
 	    ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", certdata,
@@ -1078,49 +1102,64 @@ static int tls_check_one_certificate (co
         break;
     }
   }
-  unset_option (OPTIGNOREMACROEVENTS);
+  if (reset_ignoremacro)
+    unset_option (OPTIGNOREMACROEVENTS);
+
   mutt_pop_current_menu (menu);
   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;
-  }
+                gnutls_strerror (verify_ret));
 
-  /* 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;
-  }
-
-  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;
@@ -1129,16 +1168,17 @@ static int tls_check_certificate (CONNEC
   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;
 
-  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)
@@ -1152,17 +1192,13 @@ static int tls_check_certificate (CONNEC
    * from most specific to least checking these. If we see a saved certificate,
    * its status short-circuits the remaining checks. */
   preauthrc = 0;
-  for (i = 0; i < cert_list_size; i++) {
+  for (i = 0; i < cert_list_size; i++)
+  {
     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)
     {
@@ -1179,18 +1215,26 @@ static int tls_check_certificate (CONNEC
     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) {
-      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));
-
-      certstat = tls_verify_peers (state);
-      /* If the cert chain now verifies, and the peer's cert was otherwise
-       * valid (rcpeer==0), we are done.
+    if (i)
+    {
+      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;
+
+      /* 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;
     }
   }
openSUSE Build Service is sponsored by