Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15:Update
mutt.17923
mutt-1.10.1-backport-mutt_ssl_gnutls-1.14.3.dif
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File mutt-1.10.1-backport-mutt_ssl_gnutls-1.14.3.dif of Package mutt.17923
--- 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; } }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor