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;
}
}