On Apple platforms there is no /etc/ssl/certs nor an SSL_CERT_FILE to point
a file-based trust store at, so validate the peer certificate chain against
the system keychain with SecTrust instead. The DER chain from the TLS
session is wrapped into SecCertificateRefs, evaluated with an SSL policy for
the requested host via SecTrustEvaluateWithError, and the handshake fails on
an untrusted or mismatched chain.

This is wired into the GnuTLS backend (the practical iOS configuration)
through gnutls_session_set_verify_function; non-Apple builds keep the
existing system-trust verification.
---
 libavformat/http3.c | 84 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/libavformat/http3.c b/libavformat/http3.c
index 121d221..3ed98a0 100644
--- a/libavformat/http3.c
+++ b/libavformat/http3.c
@@ -65,6 +65,10 @@
 #include <ngtcp2/ngtcp2_crypto_ossl.h>
 #endif
 #endif
+#if defined(__APPLE__)
+#include <Security/Security.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
 #include <ngtcp2/ngtcp2.h>
 #include <ngtcp2/ngtcp2_crypto.h>
 #include <nghttp3/nghttp3.h>
@@ -325,6 +329,81 @@ static int h3_end_headers_cb(nghttp3_conn *conn, int64_t 
stream_id, int fin,
 
 /* ---- connection bring-up ---- */
 
+#if CONFIG_GNUTLS && defined(__APPLE__)
+/* Verify the peer's DER certificate chain against the system keychain via
+   SecTrust. On iOS there is no /etc/ssl/certs nor SSL_CERT_FILE, so this is
+   how the chain (incl. a user-trusted local CA) is validated. */
+static int h3_apple_verify(const uint8_t **der, const size_t *der_len,
+                           size_t n, const char *host)
+{
+    CFMutableArrayRef certs;
+    CFStringRef cfhost;
+    SecPolicyRef policy;
+    SecTrustRef trust = NULL;
+    OSStatus st;
+    bool ok;
+    size_t i;
+
+    if (!n)
+        return -1;
+    certs = CFArrayCreateMutable(NULL, n, &kCFTypeArrayCallBacks);
+    if (!certs)
+        return -1;
+    for (i = 0; i < n; i++) {
+        CFDataRef d = CFDataCreate(NULL, der[i], der_len[i]);
+        SecCertificateRef c = d ? SecCertificateCreateWithData(NULL, d) : NULL;
+        if (d)
+            CFRelease(d);
+        if (!c) {
+            CFRelease(certs);
+            return -1;
+        }
+        CFArrayAppendValue(certs, c);
+        CFRelease(c);
+    }
+    cfhost = CFStringCreateWithCString(NULL, host, kCFStringEncodingUTF8);
+    policy = SecPolicyCreateSSL(true, cfhost);
+    if (cfhost)
+        CFRelease(cfhost);
+    st = SecTrustCreateWithCertificates(certs, policy, &trust);
+    if (policy)
+        CFRelease(policy);
+    CFRelease(certs);
+    if (st != errSecSuccess || !trust) {
+        if (trust)
+            CFRelease(trust);
+        return -1;
+    }
+    ok = SecTrustEvaluateWithError(trust, NULL);
+    CFRelease(trust);
+    return ok ? 0 : -1;
+}
+
+/* gnutls per-session verify hook: pull the peer DER chain and hand it to
+   SecTrust (replaces gnutls_session_set_verify_cert on Apple). */
+static int h3_gnutls_verify(gnutls_session_t session)
+{
+    ngtcp2_crypto_conn_ref *ref = gnutls_session_get_ptr(session);
+    H3Conn *hc = ref ? ref->user_data : NULL;
+    const gnutls_datum_t *peers;
+    const uint8_t *der[16];
+    size_t len[16];
+    unsigned int n = 0, i;
+
+    peers = gnutls_certificate_get_peers(session, &n);
+    if (!hc || !peers || !n)
+        return GNUTLS_E_CERTIFICATE_ERROR;
+    if (n > 16)
+        n = 16;
+    for (i = 0; i < n; i++) {
+        der[i] = peers[i].data;
+        len[i] = peers[i].size;
+    }
+    return h3_apple_verify(der, len, n, hc->host) == 0
+               ? 0 : GNUTLS_E_CERTIFICATE_ERROR;
+}
+#endif
+
 static int h3_init_tls(URLContext *h, H3Conn *hc, const char *host)
 {
     hc->conn_ref.get_conn  = h3_get_conn;
@@ -361,7 +440,12 @@ static int h3_init_tls(URLContext *h, H3Conn *hc, const 
char *host)
         gnutls_server_name_set(hc->session, GNUTLS_NAME_DNS, host, 
strlen(host)); /* SNI */
         /* verify the server cert chain + match the hostname; the handshake
            fails on an invalid/mismatched cert. */
+#if defined(__APPLE__)
+        /* validate against the system keychain (no SSL_CERT_FILE on iOS) */
+        gnutls_session_set_verify_function(hc->session, h3_gnutls_verify);
+#else
         gnutls_session_set_verify_cert(hc->session, host, 0);
+#endif
     }
 #else
     {
-- 
2.47.3

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to