diff --git a/SOURCES/lftp-4.8.4-re-newed-cert.patch b/SOURCES/lftp-4.8.4-re-newed-cert.patch new file mode 100644 index 0000000..1e94959 --- /dev/null +++ b/SOURCES/lftp-4.8.4-re-newed-cert.patch @@ -0,0 +1,261 @@ +From fd40ee3542d877c37ff129d5c9b02df21d20c6a0 Mon Sep 17 00:00:00 2001 +From: Miao Wang +Date: Sat, 9 Oct 2021 18:13:30 +0800 +Subject: [PATCH] Use gnutls_certificate_verify_peers2 to verify server + certificates + +Fixes: #641 + +Signed-off-by: Miao Wang +--- + src/lftp_ssl.cc | 207 +++++++++++------------------------------------- + src/lftp_ssl.h | 2 - + 2 files changed, 48 insertions(+), 161 deletions(-) + +diff --git a/src/lftp_ssl.cc b/src/lftp_ssl.cc +index 968d3fb26..26e91e4b9 100644 +--- a/src/lftp_ssl.cc ++++ b/src/lftp_ssl.cc +@@ -338,6 +338,16 @@ void lftp_ssl_gnutls::load_keys() + if(res<0) + Log::global->Format(0,"gnutls_certificate_set_x509_key_file(%s,%s): %s\n",cert_file,key_file,gnutls_strerror(res)); + } ++ res = gnutls_certificate_set_x509_trust(cred, instance->ca_list, instance->ca_list_size); ++ if(res < 0) ++ Log::global->Format(0, "gnutls_certificate_set_x509_trust: %s\n", gnutls_strerror(res)); ++ else ++ Log::global->Format(9, "Loaded %d CAs\n", res); ++ res = gnutls_certificate_set_x509_crl(cred, instance->crl_list, instance->crl_list_size); ++ if(res < 0) ++ Log::global->Format(0, "gnutls_certificate_set_x509_crl: %s\n", gnutls_strerror(res)); ++ else ++ Log::global->Format(9, "Loaded %d CRLs\n", res); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); + } + void lftp_ssl_gnutls::shutdown() +@@ -358,174 +368,53 @@ lftp_ssl_gnutls::~lftp_ssl_gnutls() + */ + void lftp_ssl_gnutls::verify_certificate_chain(const gnutls_datum_t *cert_chain,int cert_chain_length) + { +- int i; +- gnutls_x509_crt_t *cert=(gnutls_x509_crt_t*)alloca(cert_chain_length*sizeof(gnutls_x509_crt_t)); +- +- /* Import all the certificates in the chain to +- * native certificate format. +- */ +- for (i = 0; i < cert_chain_length; i++) +- { +- gnutls_x509_crt_init(&cert[i]); +- gnutls_x509_crt_import(cert[i],&cert_chain[i],GNUTLS_X509_FMT_DER); ++ int err; ++ unsigned int status; ++ ++ gnutls_x509_crt_t leaf_cert; ++ err = gnutls_x509_crt_init(&leaf_cert); ++ if(err < 0){ ++ set_cert_error(xstring::format("GnuTLS Error: %s", gnutls_strerror(err)), NULL); ++ goto err_out; + } +- +- /* Now verify the certificates against their issuers +- * in the chain. +- */ +- for (i = 1; i < cert_chain_length; i++) +- verify_cert2(cert[i - 1], cert[i]); +- +- /* Here we must verify the last certificate in the chain against +- * our trusted CA list. +- */ +- verify_last_cert(cert[cert_chain_length - 1]); +- +- /* Check if the name in the first certificate matches our destination! +- */ +- bool check_hostname = ResMgr::QueryBool("ssl:check-hostname", hostname); +- if(check_hostname) { +- if(!gnutls_x509_crt_check_hostname(cert[0], hostname)) +- set_cert_error(xstring::format("certificate common name doesn't match requested host name %s",quote(hostname)),get_fp(cert[0])); +- } else { +- Log::global->Format(0, "WARNING: Certificate verification: hostname checking disabled\n"); ++ gnutls_x509_crt_import(leaf_cert, &cert_chain[0], GNUTLS_X509_FMT_DER); ++ if(err < 0){ ++ set_cert_error(xstring::format("GnuTLS Error: %s", gnutls_strerror(err)), NULL); ++ goto deinit_cert; + } + +- for (i = 0; i < cert_chain_length; i++) +- gnutls_x509_crt_deinit(cert[i]); +-} +- +- +-/* Verifies a certificate against an other certificate +- * which is supposed to be it's issuer. Also checks the +- * crl_list if the certificate is revoked. +- */ +-void lftp_ssl_gnutls::verify_cert2(gnutls_x509_crt_t crt,gnutls_x509_crt_t issuer) +-{ +- int ret; +- time_t now = SMTask::now; +- size_t name_size; +- char name[256]; +- +- /* Print information about the certificates to +- * be checked. +- */ +- name_size = sizeof(name); +- gnutls_x509_crt_get_dn(crt, name, &name_size); +- +- Log::global->Format(9, "Certificate: %s\n", name); +- +- name_size = sizeof(name); +- gnutls_x509_crt_get_issuer_dn(crt, name, &name_size); +- +- Log::global->Format(9, " Issued by: %s\n", name); +- +- /* Get the DN of the issuer cert. +- */ +- name_size = sizeof(name); +- gnutls_x509_crt_get_dn(issuer, name, &name_size); +- +- Log::global->Format(9, " Checking against: %s\n", name); +- +- /* Do the actual verification. +- */ +- unsigned crt_status=0; +- unsigned issuer_status=0; +- gnutls_x509_crt_verify(crt, &issuer, 1, 0, &crt_status); +- if(crt_status&GNUTLS_CERT_SIGNER_NOT_CA) +- { +- // recheck the issuer certificate against CA +- gnutls_x509_crt_verify(issuer, instance->ca_list, instance->ca_list_size, 0, &issuer_status); +- if(issuer_status==0) +- crt_status&=~GNUTLS_CERT_SIGNER_NOT_CA; +- if(crt_status==GNUTLS_CERT_INVALID) +- crt_status=0; ++ err = gnutls_certificate_verify_peers2 (session, &status); ++ if(err < 0){ ++ set_cert_error(xstring::format("Cerificate Verification Error: %s", gnutls_strerror(err)), get_fp(leaf_cert)); ++ goto deinit_cert; + } +- if (crt_status & GNUTLS_CERT_INVALID) +- { +- char msg[256]; +- strcpy(msg,"Not trusted"); +- if(crt_status & GNUTLS_CERT_SIGNER_NOT_FOUND) +- strcat(msg,": no issuer was found"); +- if(crt_status & GNUTLS_CERT_SIGNER_NOT_CA) +- strcat(msg,": issuer is not a CA"); +- set_cert_error(msg,get_fp(crt)); +- } +- else +- Log::global->Format(9, " Trusted\n"); + +- +- /* Now check the expiration dates. +- */ +- if (gnutls_x509_crt_get_activation_time(crt) > now) +- set_cert_error("Not yet activated",get_fp(crt)); +- +- if (gnutls_x509_crt_get_expiration_time(crt) < now) +- set_cert_error("Expired",get_fp(crt)); +- +- /* Check if the certificate is revoked. +- */ +- ret = gnutls_x509_crt_check_revocation(crt, instance->crl_list, instance->crl_list_size); +- if (ret == 1) { /* revoked */ +- set_cert_error("Revoked",get_fp(crt)); +- } +-} +- +- +-/* Verifies a certificate against the trusted CA list. +- * Also checks the crl_list if the certificate is revoked. +- */ +-void lftp_ssl_gnutls::verify_last_cert(gnutls_x509_crt_t crt) +-{ +- unsigned int crt_status; +- int ret; +- time_t now = SMTask::now; +- size_t name_size; +- char name[256]; +- +- /* Print information about the certificates to +- * be checked. +- */ +- name_size = sizeof(name); +- gnutls_x509_crt_get_dn(crt, name, &name_size); +- +- Log::global->Format(9, "Certificate: %s\n", name); +- +- name_size = sizeof(name); +- gnutls_x509_crt_get_issuer_dn(crt, name, &name_size); +- +- Log::global->Format(9, " Issued by: %s\n", name); +- +- /* Do the actual verification. +- */ +- gnutls_x509_crt_verify(crt, instance->ca_list, instance->ca_list_size, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &crt_status); +- +- if (crt_status & GNUTLS_CERT_INVALID) +- { +- char msg[256]; +- strcpy(msg,"Not trusted"); +- if (crt_status & GNUTLS_CERT_SIGNER_NOT_CA) +- strcat(msg,": Issuer is not a CA"); +- set_cert_error(msg,get_fp(crt)); ++ if(status != 0){ ++ gnutls_datum_t reason; ++ err = gnutls_certificate_verification_status_print(status, gnutls_certificate_type_get(session), &reason, 0); ++ if(err < 0){ ++ set_cert_error(xstring::format("Cerificate Verification Error: %s", gnutls_strerror(err)), get_fp(leaf_cert)); ++ goto deinit_cert; ++ } ++ set_cert_error((const char*)reason.data, get_fp(leaf_cert)); ++ gnutls_free(reason.data); ++ goto deinit_cert; + } +- else +- Log::global->Format(9, " Trusted\n"); + ++ if(ResMgr::QueryBool("ssl:check-hostname", hostname)) { ++ if(!gnutls_x509_crt_check_hostname(leaf_cert, hostname)){ ++ set_cert_error(xstring::format("certificate common name doesn't match requested host name %s",quote(hostname)),get_fp(leaf_cert)); ++ goto deinit_cert; ++ } ++ } else { ++ Log::global->Format(0, "WARNING: Certificate verification: hostname checking disabled\n"); ++ } + +- /* Now check the expiration dates. +- */ +- if(gnutls_x509_crt_get_activation_time(crt) > now) +- set_cert_error("Not yet activated",get_fp(crt)); +- +- if(gnutls_x509_crt_get_expiration_time(crt) < now) +- set_cert_error("Expired",get_fp(crt)); ++ deinit_cert: ++ gnutls_x509_crt_deinit(leaf_cert); + +- /* Check if the certificate is revoked. +- */ +- ret = gnutls_x509_crt_check_revocation(crt, instance->crl_list, instance->crl_list_size); +- if (ret == 1) { /* revoked */ +- set_cert_error("Revoked",get_fp(crt)); +- } ++ err_out: ++ return; + } + + bool lftp_ssl_gnutls::check_fatal(int res) +diff --git a/src/lftp_ssl.h b/src/lftp_ssl.h +index c37b047b4..87b92d4fa 100644 +--- a/src/lftp_ssl.h ++++ b/src/lftp_ssl.h +@@ -92,8 +92,6 @@ class lftp_ssl_gnutls : public lftp_ssl_base + gnutls_session_t session; + gnutls_certificate_credentials_t cred; + void verify_certificate_chain(const gnutls_datum_t *cert_chain,int cert_chain_length); +- void verify_cert2(gnutls_x509_crt_t crt,gnutls_x509_crt_t issuer); +- void verify_last_cert(gnutls_x509_crt_t crt); + int do_handshake(); + bool check_fatal(int res); + static const xstring& get_fp(gnutls_x509_crt_t crt); diff --git a/SPECS/lftp.spec b/SPECS/lftp.spec index bbd5126..1882f3e 100644 --- a/SPECS/lftp.spec +++ b/SPECS/lftp.spec @@ -1,7 +1,7 @@ Summary: A sophisticated file transfer program Name: lftp Version: 4.8.4 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv3+ Group: Applications/Internet Source0: http://lftp.yar.ru/ftp/%{name}-%{version}.tar.xz @@ -13,6 +13,7 @@ BuildRequires: desktop-file-utils Patch1: lftp-4.0.9-date_fmt.patch Patch2: lftp-4.8.4-ssh-prompt.patch +Patch3: lftp-4.8.4-re-newed-cert.patch %description LFTP is a sophisticated ftp/http file transfer program. Like bash, it has job @@ -34,6 +35,7 @@ Utility scripts for use with lftp. %patch1 -p1 -b .date_fmt %patch2 -p1 -b .ssh-prompt +%patch3 -p1 -b .re-newed-cert #sed -i.rpath -e '/lftp_cv_openssl/s|-R.*lib||' configure sed -i.norpath -e \ @@ -103,6 +105,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Mon Jul 24 2023 Michal Ruprich - 4.8.4-3 +- Resolves: #2182418 - Connection to site fails with certificate verification error + * Tue Apr 28 2020 Michal Ruprich - 4.8.4-2 - Resolves: #1793557 - SFTP over LFTP hangs if host key of the remote system doesn't exist