From 0d312728ebe02cebeb849d3991c197f092aa21d0 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 9 Feb 2023 16:54:25 -0500 Subject: curl: Backport upstream fix for SecureTransport on macOS Backport upstream curl commit `16bb32e104d` (sectransp: fix for incomplete read/writes, 2023-01-05) to fix TLS support on macOS. Fixes: #24398 --- Utilities/cmcurl/lib/vtls/sectransp.c | 128 +++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 40 deletions(-) (limited to 'Utilities') diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index ab7965465f..d903c53d10 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -136,6 +136,15 @@ /* The last #include file should be: */ #include "memdebug.h" + +#define DEBUG_CF 0 + +#if DEBUG_CF +#define CF_DEBUGF(x) x +#else +#define CF_DEBUGF(x) do { } while(0) +#endif + /* From MacTypes.h (which we can't include because it isn't present in iOS: */ #define ioErr -36 #define paramErr -50 @@ -838,6 +847,8 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, DEBUGASSERT(data); nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); + CF_DEBUGF(infof(data, CFMSG(cf, "bio_read(len=%zu) -> %zd, result=%d"), + *dataLength, nread, result)); if(nread < 0) { switch(result) { case CURLE_OK: @@ -851,6 +862,9 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, } nread = 0; } + else if((size_t)nread < *dataLength) { + rtn = errSSLWouldBlock; + } *dataLength = nread; return rtn; } @@ -865,22 +879,27 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection, struct Curl_easy *data = connssl->call_data; ssize_t nwritten; CURLcode result; - OSStatus ortn = noErr; + OSStatus rtn = noErr; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); + CF_DEBUGF(infof(data, CFMSG(cf, "bio_send(len=%zu) -> %zd, result=%d"), + *dataLength, nwritten, result)); if(nwritten <= 0) { if(result == CURLE_AGAIN) { - ortn = errSSLWouldBlock; + rtn = errSSLWouldBlock; backend->ssl_direction = true; } else { - ortn = ioErr; + rtn = ioErr; } nwritten = 0; } + else if((size_t)nwritten < *dataLength) { + rtn = errSSLWouldBlock; + } *dataLength = nwritten; - return ortn; + return rtn; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -1638,6 +1657,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); + CF_DEBUGF(infof(data, CFMSG(cf, "connect_step1"))); GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -2231,7 +2251,8 @@ static int append_cert_to_array(struct Curl_easy *data, return CURLE_OK; } -static CURLcode verify_cert_buf(struct Curl_easy *data, +static CURLcode verify_cert_buf(struct Curl_cfilter *cf, + struct Curl_easy *data, const unsigned char *certbuf, size_t buflen, SSLContextRef ctx) { @@ -2239,7 +2260,12 @@ static CURLcode verify_cert_buf(struct Curl_easy *data, long res; unsigned char *der; size_t derlen, offset = 0; - + OSStatus ret; + SecTrustResultType trust_eval; + CFMutableArrayRef array = NULL; + SecTrustRef trust = NULL; + CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + (void)cf; /* * Certbuf now contains the contents of the certificate file, which can be * - a single DER certificate, @@ -2249,11 +2275,11 @@ static CURLcode verify_cert_buf(struct Curl_easy *data, * Go through certbuf, and convert any PEM certificate in it into DER * format. */ - CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeArrayCallBacks); + array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if(!array) { failf(data, "SSL: out of memory creating CA certificate array"); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } while(offset < buflen) { @@ -2265,10 +2291,10 @@ static CURLcode verify_cert_buf(struct Curl_easy *data, */ res = pem_to_der((const char *)certbuf + offset, &der, &derlen); if(res < 0) { - CFRelease(array); failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", n, offset); - return CURLE_SSL_CACERT_BADFILE; + result = CURLE_SSL_CACERT_BADFILE; + goto out; } offset += res; @@ -2276,8 +2302,9 @@ static CURLcode verify_cert_buf(struct Curl_easy *data, /* This is not a PEM file, probably a certificate in DER format. */ rc = append_cert_to_array(data, certbuf, buflen, array); if(rc != CURLE_OK) { - CFRelease(array); - return rc; + CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed"))); + result = rc; + goto out; } break; } @@ -2289,63 +2316,73 @@ static CURLcode verify_cert_buf(struct Curl_easy *data, rc = append_cert_to_array(data, der, derlen, array); free(der); if(rc != CURLE_OK) { - CFRelease(array); - return rc; + CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed"))); + result = rc; + goto out; } } - SecTrustRef trust; - OSStatus ret = SSLCopyPeerTrust(ctx, &trust); + ret = SSLCopyPeerTrust(ctx, &trust); if(!trust) { failf(data, "SSL: error getting certificate chain"); - CFRelease(array); - return CURLE_PEER_FAILED_VERIFICATION; + goto out; } else if(ret != noErr) { - CFRelease(array); failf(data, "SSLCopyPeerTrust() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; + goto out; } + CF_DEBUGF(infof(data, CFMSG(cf, "setting %d trust anchors"), n)); ret = SecTrustSetAnchorCertificates(trust, array); if(ret != noErr) { - CFRelease(array); - CFRelease(trust); failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; + goto out; } ret = SecTrustSetAnchorCertificatesOnly(trust, true); if(ret != noErr) { - CFRelease(array); - CFRelease(trust); failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; + goto out; } - SecTrustResultType trust_eval = 0; + trust_eval = 0; ret = SecTrustEvaluate(trust, &trust_eval); - CFRelease(array); - CFRelease(trust); if(ret != noErr) { failf(data, "SecTrustEvaluate() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; + goto out; } switch(trust_eval) { case kSecTrustResultUnspecified: + /* what does this really mean? */ + CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Unspecified"))); + result = CURLE_OK; + goto out; case kSecTrustResultProceed: - return CURLE_OK; + CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Proceed"))); + result = CURLE_OK; + goto out; case kSecTrustResultRecoverableTrustFailure: + failf(data, "SSL: peer not verified: RecoverableTrustFailure"); + goto out; case kSecTrustResultDeny: + failf(data, "SSL: peer not verified: Deny"); + goto out; default: - failf(data, "SSL: certificate verification failed (result: %d)", - trust_eval); - return CURLE_PEER_FAILED_VERIFICATION; + failf(data, "SSL: perr not verified: result=%d", trust_eval); + goto out; } + +out: + if(trust) + CFRelease(trust); + if(array) + CFRelease(array); + return result; } -static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, +static CURLcode verify_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, const char *cafile, const struct curl_blob *ca_info_blob, SSLContextRef ctx) { @@ -2354,6 +2391,7 @@ static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, size_t buflen; if(ca_info_blob) { + CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from config blob"))); certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); if(!certbuf) { return CURLE_OUT_OF_MEMORY; @@ -2363,6 +2401,8 @@ static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, certbuf[ca_info_blob->len]='\0'; } else if(cafile) { + CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from file '%s'"), + cafile)); if(read_cert(cafile, &certbuf, &buflen) < 0) { failf(data, "SSL: failed to read or invalid CA certificate"); return CURLE_SSL_CACERT_BADFILE; @@ -2371,7 +2411,7 @@ static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, else return CURLE_SSL_CACERT_BADFILE; - result = verify_cert_buf(data, certbuf, buflen, ctx); + result = verify_cert_buf(cf, data, certbuf, buflen, ctx); free(certbuf); return result; } @@ -2498,8 +2538,10 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); DEBUGASSERT(backend); + CF_DEBUGF(infof(data, CFMSG(cf, "connect_step2"))); /* Here goes nothing: */ +check_handshake: err = SSLHandshake(backend->ssl_ctx); if(err != noErr) { @@ -2514,14 +2556,14 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, case -9841: if((conn_config->CAfile || conn_config->ca_info_blob) && conn_config->verifypeer) { - CURLcode result = verify_cert(data, conn_config->CAfile, + CURLcode result = verify_cert(cf, data, conn_config->CAfile, conn_config->ca_info_blob, backend->ssl_ctx); if(result) return result; } /* the documentation says we need to call SSLHandshake() again */ - return sectransp_connect_step2(cf, data); + goto check_handshake; /* Problem with encrypt / decrypt */ case errSSLPeerDecodeError: @@ -2961,6 +3003,7 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; + CF_DEBUGF(infof(data, CFMSG(cf, "connect_step3"))); /* There is no step 3! * Well, okay, let's collect server certificates, and if verbose mode is on, * let's print the details of the server certificates. */ @@ -3069,6 +3112,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } if(ssl_connect_done == connssl->connecting_state) { + CF_DEBUGF(infof(data, CFMSG(cf, "connected"))); connssl->state = ssl_connection_complete; *done = TRUE; } @@ -3114,6 +3158,7 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); if(backend->ssl_ctx) { + CF_DEBUGF(infof(data, CFMSG(cf, "close"))); (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext) @@ -3157,6 +3202,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], SSL_SHUTDOWN_TIMEOUT); + CF_DEBUGF(infof(data, CFMSG(cf, "shutdown"))); while(loop--) { if(what < 0) { /* anything that gets here is fatally bad */ @@ -3225,6 +3271,7 @@ static int sectransp_check_cxn(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ssl_ctx) { + CF_DEBUGF(infof(data, CFMSG(cf, "check connection"))); err = SSLGetSessionState(backend->ssl_ctx, &state); if(err == noErr) return state == kSSLConnected || state == kSSLHandshake; @@ -3245,6 +3292,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ssl_ctx) { /* SSL is in use */ + CF_DEBUGF(infof(data, CFMSG(cf, "data_pending"))); err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; @@ -3402,7 +3450,7 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf, case -9841: if((conn_config->CAfile || conn_config->ca_info_blob) && conn_config->verifypeer) { - CURLcode result = verify_cert(data, conn_config->CAfile, + CURLcode result = verify_cert(cf, data, conn_config->CAfile, conn_config->ca_info_blob, backend->ssl_ctx); if(result) -- cgit v1.2.1