diff --git a/backport-CVE-2020-8285.patch b/backport-CVE-2020-8285.patch new file mode 100644 index 0000000000000000000000000000000000000000..0cbc9508999390115e56a91b44aa69d972101cde --- /dev/null +++ b/backport-CVE-2020-8285.patch @@ -0,0 +1,248 @@ +From 69a358f2186e04cf44698b5100332cbf1ee7f01d Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Sat, 28 Nov 2020 00:27:21 +0100 +Subject: [PATCH] ftp: make wc_statemach loop instead of recurse + +Fixes #6255 +Bug: https://curl.se/docs/CVE-2020-8285.html +Reported-by: xnynx on github +--- + lib/ftp.c | 202 +++++++++++++++++++++++++++--------------------------- + 1 file changed, 102 insertions(+), 100 deletions(-) + +diff --git a/lib/ftp.c b/lib/ftp.c +index 50e7d7ddac9..bc355742172 100644 +--- a/lib/ftp.c ++++ b/lib/ftp.c +@@ -3800,129 +3800,131 @@ static CURLcode init_wc_data(struct connectdata *conn) + return result; + } + +-/* This is called recursively */ + static CURLcode wc_statemach(struct connectdata *conn) + { + struct WildcardData * const wildcard = &(conn->data->wildcard); + CURLcode result = CURLE_OK; + +- switch(wildcard->state) { +- case CURLWC_INIT: +- result = init_wc_data(conn); +- if(wildcard->state == CURLWC_CLEAN) +- /* only listing! */ +- break; +- wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; +- break; ++ for(;;) { ++ switch(wildcard->state) { ++ case CURLWC_INIT: ++ result = init_wc_data(conn); ++ if(wildcard->state == CURLWC_CLEAN) ++ /* only listing! */ ++ return result; ++ wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; ++ return result; + +- case CURLWC_MATCHING: { +- /* In this state is LIST response successfully parsed, so lets restore +- previous WRITEFUNCTION callback and WRITEDATA pointer */ +- struct ftp_wc *ftpwc = wildcard->protdata; +- conn->data->set.fwrite_func = ftpwc->backup.write_function; +- conn->data->set.out = ftpwc->backup.file_descriptor; +- ftpwc->backup.write_function = ZERO_NULL; +- ftpwc->backup.file_descriptor = NULL; +- wildcard->state = CURLWC_DOWNLOADING; +- +- if(Curl_ftp_parselist_geterror(ftpwc->parser)) { +- /* error found in LIST parsing */ +- wildcard->state = CURLWC_CLEAN; +- return wc_statemach(conn); +- } +- if(wildcard->filelist.size == 0) { +- /* no corresponding file */ +- wildcard->state = CURLWC_CLEAN; +- return CURLE_REMOTE_FILE_NOT_FOUND; ++ case CURLWC_MATCHING: { ++ /* In this state is LIST response successfully parsed, so lets restore ++ previous WRITEFUNCTION callback and WRITEDATA pointer */ ++ struct ftp_wc *ftpwc = wildcard->protdata; ++ conn->data->set.fwrite_func = ftpwc->backup.write_function; ++ conn->data->set.out = ftpwc->backup.file_descriptor; ++ ftpwc->backup.write_function = ZERO_NULL; ++ ftpwc->backup.file_descriptor = NULL; ++ wildcard->state = CURLWC_DOWNLOADING; ++ ++ if(Curl_ftp_parselist_geterror(ftpwc->parser)) { ++ /* error found in LIST parsing */ ++ wildcard->state = CURLWC_CLEAN; ++ continue; ++ } ++ if(wildcard->filelist.size == 0) { ++ /* no corresponding file */ ++ wildcard->state = CURLWC_CLEAN; ++ return CURLE_REMOTE_FILE_NOT_FOUND; ++ } ++ continue; + } +- return wc_statemach(conn); +- } + +- case CURLWC_DOWNLOADING: { +- /* filelist has at least one file, lets get first one */ +- struct ftp_conn *ftpc = &conn->proto.ftpc; +- struct curl_fileinfo *finfo = wildcard->filelist.head->ptr; +- struct FTP *ftp = conn->data->req.protop; ++ case CURLWC_DOWNLOADING: { ++ /* filelist has at least one file, lets get first one */ ++ struct ftp_conn *ftpc = &conn->proto.ftpc; ++ struct curl_fileinfo *finfo = wildcard->filelist.head->ptr; ++ struct FTP *ftp = conn->data->req.protop; + +- char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); +- if(!tmp_path) +- return CURLE_OUT_OF_MEMORY; ++ char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); ++ if(!tmp_path) ++ return CURLE_OUT_OF_MEMORY; + +- /* switch default ftp->path and tmp_path */ +- free(ftp->pathalloc); +- ftp->pathalloc = ftp->path = tmp_path; +- +- infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename); +- if(conn->data->set.chunk_bgn) { +- long userresponse; +- Curl_set_in_callback(conn->data, true); +- userresponse = conn->data->set.chunk_bgn( +- finfo, wildcard->customptr, (int)wildcard->filelist.size); +- Curl_set_in_callback(conn->data, false); +- switch(userresponse) { +- case CURL_CHUNK_BGN_FUNC_SKIP: +- infof(conn->data, "Wildcard - \"%s\" skipped by user\n", +- finfo->filename); +- wildcard->state = CURLWC_SKIP; +- return wc_statemach(conn); +- case CURL_CHUNK_BGN_FUNC_FAIL: +- return CURLE_CHUNK_FAILED; ++ /* switch default ftp->path and tmp_path */ ++ free(ftp->pathalloc); ++ ftp->pathalloc = ftp->path = tmp_path; ++ ++ infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename); ++ if(conn->data->set.chunk_bgn) { ++ long userresponse; ++ Curl_set_in_callback(conn->data, true); ++ userresponse = conn->data->set.chunk_bgn( ++ finfo, wildcard->customptr, (int)wildcard->filelist.size); ++ Curl_set_in_callback(conn->data, false); ++ switch(userresponse) { ++ case CURL_CHUNK_BGN_FUNC_SKIP: ++ infof(conn->data, "Wildcard - \"%s\" skipped by user\n", ++ finfo->filename); ++ wildcard->state = CURLWC_SKIP; ++ continue; ++ case CURL_CHUNK_BGN_FUNC_FAIL: ++ return CURLE_CHUNK_FAILED; ++ } + } +- } + +- if(finfo->filetype != CURLFILETYPE_FILE) { +- wildcard->state = CURLWC_SKIP; +- return wc_statemach(conn); +- } ++ if(finfo->filetype != CURLFILETYPE_FILE) { ++ wildcard->state = CURLWC_SKIP; ++ continue; ++ } + +- if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) +- ftpc->known_filesize = finfo->size; ++ if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) ++ ftpc->known_filesize = finfo->size; + +- result = ftp_parse_url_path(conn); +- if(result) +- return result; ++ result = ftp_parse_url_path(conn); ++ if(result) ++ return result; + +- /* we don't need the Curl_fileinfo of first file anymore */ +- Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); ++ /* we don't need the Curl_fileinfo of first file anymore */ ++ Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); + +- if(wildcard->filelist.size == 0) { /* remains only one file to down. */ +- wildcard->state = CURLWC_CLEAN; +- /* after that will be ftp_do called once again and no transfer +- will be done because of CURLWC_CLEAN state */ +- return CURLE_OK; ++ if(wildcard->filelist.size == 0) { /* remains only one file to down. */ ++ wildcard->state = CURLWC_CLEAN; ++ /* after that will be ftp_do called once again and no transfer ++ will be done because of CURLWC_CLEAN state */ ++ return CURLE_OK; ++ } ++ return result; + } +- } break; + +- case CURLWC_SKIP: { +- if(conn->data->set.chunk_end) { +- Curl_set_in_callback(conn->data, true); +- conn->data->set.chunk_end(conn->data->wildcard.customptr); +- Curl_set_in_callback(conn->data, false); ++ case CURLWC_SKIP: { ++ if(conn->data->set.chunk_end) { ++ Curl_set_in_callback(conn->data, true); ++ conn->data->set.chunk_end(conn->data->wildcard.customptr); ++ Curl_set_in_callback(conn->data, false); ++ } ++ Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); ++ wildcard->state = (wildcard->filelist.size == 0) ? ++ CURLWC_CLEAN : CURLWC_DOWNLOADING; ++ continue; + } +- Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); +- wildcard->state = (wildcard->filelist.size == 0) ? +- CURLWC_CLEAN : CURLWC_DOWNLOADING; +- return wc_statemach(conn); +- } + +- case CURLWC_CLEAN: { +- struct ftp_wc *ftpwc = wildcard->protdata; +- result = CURLE_OK; +- if(ftpwc) +- result = Curl_ftp_parselist_geterror(ftpwc->parser); ++ case CURLWC_CLEAN: { ++ struct ftp_wc *ftpwc = wildcard->protdata; ++ result = CURLE_OK; ++ if(ftpwc) ++ result = Curl_ftp_parselist_geterror(ftpwc->parser); + +- wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; +- } break; ++ wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; ++ return result; ++ } + +- case CURLWC_DONE: +- case CURLWC_ERROR: +- case CURLWC_CLEAR: +- if(wildcard->dtor) +- wildcard->dtor(wildcard->protdata); +- break; ++ case CURLWC_DONE: ++ case CURLWC_ERROR: ++ case CURLWC_CLEAR: ++ if(wildcard->dtor) ++ wildcard->dtor(wildcard->protdata); ++ return result; ++ } + } +- +- return result; ++ /* UNREACHABLE */ + } + + /*********************************************************************** diff --git a/backport-CVE-2020-8286.patch b/backport-CVE-2020-8286.patch new file mode 100644 index 0000000000000000000000000000000000000000..730af8ab3b259d363152803a487e84c12517f28c --- /dev/null +++ b/backport-CVE-2020-8286.patch @@ -0,0 +1,121 @@ +From d9d01672785b8ac04aab1abb6de95fe3072ae199 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Wed, 2 Dec 2020 23:01:11 +0100 +Subject: [PATCH] openssl: make the OCSP verification verify the certificate id + +Reported by anonymous + +Bug: https://curl.se/docs/CVE-2020-8286.html +--- + lib/vtls/openssl.c | 83 ++++++++++++++++++++++++++++++---------------- + 1 file changed, 54 insertions(+), 29 deletions(-) + +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index c905465a0cf..e9c535f8f47 100644 +--- a/lib/vtls/openssl.c ++++ b/lib/vtls/openssl.c +@@ -1795,6 +1795,11 @@ static CURLcode verifystatus(struct connectdata *conn, + X509_STORE *st = NULL; + STACK_OF(X509) *ch = NULL; + struct ssl_backend_data *backend = connssl->backend; ++ X509 *cert; ++ OCSP_CERTID *id = NULL; ++ int cert_status, crl_reason; ++ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; ++ int ret; + + long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); + +@@ -1863,43 +1868,63 @@ static CURLcode verifystatus(struct connectdata *conn, + goto end; + } + +- for(i = 0; i < OCSP_resp_count(br); i++) { +- int cert_status, crl_reason; +- OCSP_SINGLERESP *single = NULL; +- +- ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; ++ /* Compute the certificate's ID */ ++ cert = SSL_get_peer_certificate(backend->handle); ++ if(!cert) { ++ failf(data, "Error getting peer certficate"); ++ result = CURLE_SSL_INVALIDCERTSTATUS; ++ goto end; ++ } + +- single = OCSP_resp_get0(br, i); +- if(!single) +- continue; ++ for(i = 0; i < sk_X509_num(ch); i++) { ++ X509 *issuer = sk_X509_value(ch, i); ++ if(X509_check_issued(issuer, cert) == X509_V_OK) { ++ id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); ++ break; ++ } ++ } ++ X509_free(cert); + +- cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, +- &thisupd, &nextupd); ++ if(!id) { ++ failf(data, "Error computing OCSP ID"); ++ result = CURLE_SSL_INVALIDCERTSTATUS; ++ goto end; ++ } + +- if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { +- failf(data, "OCSP response has expired"); +- result = CURLE_SSL_INVALIDCERTSTATUS; +- goto end; +- } ++ /* Find the single OCSP response corresponding to the certificate ID */ ++ ret = OCSP_resp_find_status(br, id, &cert_status, &crl_reason, &rev, ++ &thisupd, &nextupd); ++ OCSP_CERTID_free(id); ++ if(ret != 1) { ++ failf(data, "Could not find certificate ID in OCSP response"); ++ result = CURLE_SSL_INVALIDCERTSTATUS; ++ goto end; ++ } + +- infof(data, "SSL certificate status: %s (%d)\n", +- OCSP_cert_status_str(cert_status), cert_status); ++ /* Validate the corresponding single OCSP response */ ++ if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { ++ failf(data, "OCSP response has expired"); ++ result = CURLE_SSL_INVALIDCERTSTATUS; ++ goto end; ++ } + +- switch(cert_status) { +- case V_OCSP_CERTSTATUS_GOOD: +- break; ++ infof(data, "SSL certificate status: %s (%d)\n", ++ OCSP_cert_status_str(cert_status), cert_status); + +- case V_OCSP_CERTSTATUS_REVOKED: +- result = CURLE_SSL_INVALIDCERTSTATUS; ++ switch(cert_status) { ++ case V_OCSP_CERTSTATUS_GOOD: ++ break; + +- failf(data, "SSL certificate revocation reason: %s (%d)", +- OCSP_crl_reason_str(crl_reason), crl_reason); +- goto end; ++ case V_OCSP_CERTSTATUS_REVOKED: ++ result = CURLE_SSL_INVALIDCERTSTATUS; ++ failf(data, "SSL certificate revocation reason: %s (%d)", ++ OCSP_crl_reason_str(crl_reason), crl_reason); ++ goto end; + +- case V_OCSP_CERTSTATUS_UNKNOWN: +- result = CURLE_SSL_INVALIDCERTSTATUS; +- goto end; +- } ++ case V_OCSP_CERTSTATUS_UNKNOWN: ++ default: ++ result = CURLE_SSL_INVALIDCERTSTATUS; ++ goto end; + } + + end: diff --git a/curl.spec b/curl.spec index 306f3560641b365932b142089a9290031a60a92b..837a5f29dec581cc74a44734413d57acd3a58fda 100644 --- a/curl.spec +++ b/curl.spec @@ -6,7 +6,7 @@ Name: curl Version: 7.71.1 -Release: 4 +Release: 5 Summary: Curl is used in command lines or scripts to transfer data License: MIT URL: https://curl.haxx.se/ @@ -20,6 +20,8 @@ Patch106: 0106-curl-fix-CVE-2019-15601.patch Patch107: 0107-curl-close-unused-connect-only-connections.patch Patch108: 0108-curl-fix-CVE-2020-8231.patch Patch109: backport-CVE-2020-8284.patch +Patch110: backport-CVE-2020-8285.patch +Patch111: backport-CVE-2020-8286.patch BuildRequires: automake brotli-devel coreutils gcc groff krb5-devel BuildRequires: libidn2-devel libmetalink-devel libnghttp2-devel libpsl-devel @@ -161,6 +163,12 @@ rm -rf ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la %{_mandir}/man3/* %changelog +* Mon Feb 2021 wangxiaopeng - 7.71.1-5 +- Type:CVE +- CVE:CVE-2020-8285 CVE-2020-8286 +- SUG:NA +- DESC:fix CVE-2020-8285 CVE-2020-8286 + * Mon Jan 18 2021 xihaochen - 7.71.1-4 - Type:CVE - CVE:CVE-2020-8284