| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at https://curl.se/docs/copyright.html. |
| * |
| * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| * copies of the Software, and permit persons to whom the Software is |
| * furnished to do so, under the terms of the COPYING file. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| * SPDX-License-Identifier: curl |
| * |
| ***************************************************************************/ |
| |
| /* This file is for implementing all "generic" SSL functions that all libcurl |
| internals should use. It is then responsible for calling the proper |
| "backend" function. |
| |
| SSL-functions in libcurl should call functions in this source file, and not |
| to any specific SSL-layer. |
| |
| Curl_ssl_ - prefix for generic ones |
| |
| Note that this source code uses the functions of the configured SSL |
| backend via the global Curl_ssl instance. |
| |
| "SSL/TLS Strong Encryption: An Introduction" |
| https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html |
| */ |
| |
| #include "curl_setup.h" |
| |
| #ifdef USE_APPLE_SECTRUST |
| |
| #include "urldata.h" |
| #include "cfilters.h" |
| #include "curl_trc.h" |
| #include "vtls/vtls.h" |
| #include "vtls/apple.h" |
| |
| #include <Security/Security.h> |
| |
| #if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && \ |
| MAC_OS_X_VERSION_MAX_ALLOWED >= 101400) || \ |
| (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ |
| __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000) |
| #define SUPPORTS_SecTrustEvaluateWithError 1 |
| #endif |
| |
| #if defined(SUPPORTS_SecTrustEvaluateWithError) && \ |
| ((defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ |
| MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || \ |
| (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ |
| __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)) |
| #define REQUIRES_SecTrustEvaluateWithError 1 |
| #endif |
| |
| #if defined(SUPPORTS_SecTrustEvaluateWithError) && \ |
| !defined(HAVE_BUILTIN_AVAILABLE) && \ |
| !defined(REQUIRES_SecTrustEvaluateWithError) |
| #undef SUPPORTS_SecTrustEvaluateWithError |
| #endif |
| |
| #if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && \ |
| MAC_OS_X_VERSION_MAX_ALLOWED >= 100900) || \ |
| (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ |
| __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) |
| #define SUPPORTS_SecOCSP 1 |
| #endif |
| |
| CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, |
| struct Curl_easy *data, |
| struct ssl_peer *peer, |
| size_t num_certs, |
| Curl_vtls_get_cert_der *der_cb, |
| void *cb_user_data, |
| const unsigned char *ocsp_buf, |
| size_t ocsp_len) |
| { |
| struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); |
| CURLcode result = CURLE_OK; |
| SecTrustRef trust = NULL; |
| SecPolicyRef policy = NULL; |
| CFMutableArrayRef policies = NULL; |
| CFMutableArrayRef cert_array = NULL; |
| CFStringRef host_str = NULL; |
| CFErrorRef error = NULL; |
| OSStatus status = noErr; |
| CFStringRef error_ref = NULL; |
| char *err_desc = NULL; |
| size_t i; |
| |
| if(conn_config->verifyhost) { |
| host_str = CFStringCreateWithCString(NULL, |
| peer->sni ? peer->sni : peer->hostname, kCFStringEncodingUTF8); |
| if(!host_str) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| } |
| |
| policies = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks); |
| if(!policies) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| policy = SecPolicyCreateSSL(true, host_str); |
| if(!policy) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| CFArrayAppendValue(policies, policy); |
| CFRelease(policy); |
| policy = NULL; |
| |
| #if defined(HAVE_BUILTIN_AVAILABLE) && defined(SUPPORTS_SecOCSP) |
| { |
| struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); |
| if(!ssl_config->no_revoke) { |
| if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { |
| /* Even without this set, validation seemingly-unavoidably fails |
| * for certificates that trustd already knows to be revoked. |
| * This policy further allows trustd to consult CRLs and OCSP data |
| * to determine revocation status (which it may then cache). */ |
| CFOptionFlags revocation_flags = kSecRevocationUseAnyAvailableMethod; |
| #if 0 |
| /* `revoke_best_effort` is off by default in libcurl. When we |
| * add `kSecRevocationRequirePositiveResponse` to the Apple |
| * Trust policies, it interprets this as it NEEDs a confirmation |
| * of a cert being NOT REVOKED. Which not in general available for |
| * certificates on the Internet. |
| * It seems that applications using this policy are expected to PIN |
| * their certificate public keys or verification fails. |
| * This does not seem to be what we want here. */ |
| if(!ssl_config->revoke_best_effort) { |
| revocation_flags |= kSecRevocationRequirePositiveResponse; |
| } |
| #endif |
| policy = SecPolicyCreateRevocation(revocation_flags); |
| if(!policy) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| CFArrayAppendValue(policies, policy); |
| } |
| } |
| } |
| #endif |
| |
| cert_array = CFArrayCreateMutable(NULL, num_certs, &kCFTypeArrayCallBacks); |
| if(!cert_array) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| for(i = 0; i < num_certs; i++) { |
| SecCertificateRef cert; |
| CFDataRef certdata; |
| unsigned char *der; |
| size_t der_len; |
| |
| result = der_cb(cf, data, cb_user_data, i, &der, &der_len); |
| if(result) |
| goto out; |
| |
| certdata = CFDataCreate(NULL, der, (CFIndex)der_len); |
| if(!certdata) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| cert = SecCertificateCreateWithData(NULL, certdata); |
| CFRelease(certdata); |
| if(!cert) { |
| result = CURLE_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| CFArrayAppendValue(cert_array, cert); |
| CFRelease(cert); |
| } |
| |
| status = SecTrustCreateWithCertificates(cert_array, policies, &trust); |
| if(status != noErr || !trust) { |
| failf(data, "Apple SecTrust: failed to create validation trust"); |
| result = CURLE_PEER_FAILED_VERIFICATION; |
| goto out; |
| } |
| |
| #if defined(HAVE_BUILTIN_AVAILABLE) && defined(SUPPORTS_SecOCSP) |
| if(ocsp_len > 0) { |
| if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { |
| CFDataRef ocspdata = CFDataCreate(NULL, ocsp_buf, (CFIndex)ocsp_len); |
| |
| status = SecTrustSetOCSPResponse(trust, ocspdata); |
| CFRelease(ocspdata); |
| if(status != noErr) { |
| failf(data, "Apple SecTrust: failed to set OCSP response: %i", |
| (int)status); |
| result = CURLE_PEER_FAILED_VERIFICATION; |
| goto out; |
| } |
| } |
| } |
| #else |
| (void)ocsp_buf; |
| (void)ocsp_len; |
| #endif |
| |
| #ifdef SUPPORTS_SecTrustEvaluateWithError |
| #ifdef HAVE_BUILTIN_AVAILABLE |
| if(__builtin_available(macOS 10.14, iOS 12, tvOS 12, watchOS 5, *)) { |
| #else |
| if(1) { |
| #endif |
| result = SecTrustEvaluateWithError(trust, &error) ? |
| CURLE_OK : CURLE_PEER_FAILED_VERIFICATION; |
| if(error) { |
| VERBOSE(CFIndex code = CFErrorGetCode(error)); |
| error_ref = CFErrorCopyDescription(error); |
| |
| if(error_ref) { |
| CFIndex size = CFStringGetMaximumSizeForEncoding( |
| CFStringGetLength(error_ref), kCFStringEncodingUTF8); |
| err_desc = curlx_malloc(size + 1); |
| if(err_desc) { |
| if(!CFStringGetCString(error_ref, err_desc, size, |
| kCFStringEncodingUTF8)) { |
| curlx_free(err_desc); |
| err_desc = NULL; |
| } |
| } |
| } |
| infof(data, "Apple SecTrust failure %ld%s%s", code, |
| err_desc ? ": " : "", err_desc ? err_desc : ""); |
| } |
| } |
| else |
| #endif /* SUPPORTS_SecTrustEvaluateWithError */ |
| { |
| #ifndef REQUIRES_SecTrustEvaluateWithError |
| SecTrustResultType sec_result; |
| status = SecTrustEvaluate(trust, &sec_result); |
| |
| if(status != noErr) { |
| failf(data, "Apple SecTrust verification failed: error %i", (int)status); |
| result = CURLE_PEER_FAILED_VERIFICATION; |
| } |
| else if((sec_result == kSecTrustResultUnspecified) || |
| (sec_result == kSecTrustResultProceed)) { |
| /* "unspecified" means system-trusted with no explicit user setting */ |
| result = CURLE_OK; |
| } |
| else { |
| /* Any other trust result is a verification failure in this context */ |
| result = CURLE_PEER_FAILED_VERIFICATION; |
| } |
| #endif /* REQUIRES_SecTrustEvaluateWithError */ |
| } |
| |
| out: |
| curlx_free(err_desc); |
| if(error_ref) |
| CFRelease(error_ref); |
| if(error) |
| CFRelease(error); |
| if(host_str) |
| CFRelease(host_str); |
| if(policies) |
| CFRelease(policies); |
| if(policy) |
| CFRelease(policy); |
| if(cert_array) |
| CFRelease(cert_array); |
| if(trust) |
| CFRelease(trust); |
| return result; |
| } |
| |
| #endif /* USE_APPLE_SECTRUST */ |