[vlc-devel] [PATCH 2/2] securetransport: Add ALPN support

Marvin Scholz epirat07 at gmail.com
Tue Mar 27 20:16:37 CEST 2018


---
 modules/misc/securetransport.c | 149 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 144 insertions(+), 5 deletions(-)

diff --git a/modules/misc/securetransport.c b/modules/misc/securetransport.c
index fcd5e8bc72..11bb43228d 100644
--- a/modules/misc/securetransport.c
+++ b/modules/misc/securetransport.c
@@ -40,6 +40,80 @@
 # define ioErr -36
 #endif
 
+/*****************************************************************************
+ * ALPN helper functions
+ *****************************************************************************/
+
+/* Converts the VLC ALPN C array (null-terminated) to a ALPN
+ * CFMutableArray as expected by the Secure Transport API
+ * Returns CFMutableArrayRef on success, else NULL.
+ */
+static CFMutableArrayRef alpnToCFArray(const char *const *alpn)
+{
+    CFMutableArrayRef alpnValues =
+            CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+    for (size_t i = 0; alpn[i] != NULL; i++) {
+        CFStringRef alpnVal =
+                CFStringCreateWithCString(kCFAllocatorDefault, alpn[i], kCFStringEncodingASCII);
+        if (alpnVal == NULL) {
+            // Failed to convert the ALPN value to CFString, error out.
+            CFRelease(alpnValues);
+            return NULL;
+        }
+        CFArrayAppendValue(alpnValues, alpnVal);
+        CFRelease(alpnVal);
+    }
+    return alpnValues;
+}
+
+/* Obtains a copy of the contents of a CFString in ASCII encoding.
+ * Returns char* (must be freed by caller) or NULL on failure.
+ */
+static char* CFStringCopyASCIICString(CFStringRef cfString)
+{
+    // Try the quick way to obtain the buffer
+    const char *tmpBuffer = CFStringGetCStringPtr(cfString, kCFStringEncodingASCII);
+
+    if (tmpBuffer != NULL) {
+       return strdup(tmpBuffer);
+    }
+
+    // The quick way did not work, try the long way
+    CFIndex length = CFStringGetLength(cfString);
+    CFIndex maxSize =
+        CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingASCII);
+
+    // If result would exceed LONG_MAX, kCFNotFound is returned
+    if (unlikely(maxSize == kCFNotFound)) {
+        return NULL;
+    }
+
+    // Account for the null terminator
+    maxSize++;
+
+    char *buffer = (char *)malloc(maxSize);
+    Boolean success = CFStringGetCString(cfString, buffer, maxSize, kCFStringEncodingASCII);
+
+    if (!success)
+        FREENULL(buffer);
+    return buffer;
+}
+
+/* Returns the first entry copy of the ALPN array as char*
+ * or NULL on failure.
+ */
+static char* CFArrayALPNCopyFirst(CFArrayRef alpnArray)
+{
+    CFIndex count = CFArrayGetCount(alpnArray);
+
+    if (count <= 0)
+        return NULL;
+
+    CFStringRef alpnVal = CFArrayGetValueAtIndex(alpnArray, 0);
+    return CFStringCopyASCIICString(alpnVal);
+}
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
@@ -393,10 +467,39 @@ static int st_Handshake (vlc_tls_creds_t *crd, vlc_tls_t *session,
     VLC_UNUSED(service);
 
     OSStatus retValue = SSLHandshake(sys->p_context);
+
+// Only try to use ALPN on recent enough SDKs
+// macOS 10.13.2, iOS 11, tvOS 11, watchOS 4
+#if (TARGET_OS_OSX    && MAC_OS_X_VERSION_MAX_ALLOWED     >= 101302) || \
+    (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED  >= 110000) || \
+    (TARGET_OS_TV     && __TV_OS_VERSION_MAX_ALLOWED      >= 110000) || \
+    (TARGET_OS_WATCH  && __WATCH_OS_VERSION_MAX_ALLOWED   >= 40000)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+
+    /* Handle ALPN data */
+    if (alp != NULL) {
+        CFArrayRef alpnArray = NULL;
+        OSStatus res = SSLCopyALPNProtocols(sys->p_context, &alpnArray);
+        if (res == noErr && alpnArray) {
+            *alp = CFArrayALPNCopyFirst(alpnArray);
+            if (unlikely(*alp == NULL))
+                return -1;
+        } else {
+            *alp = NULL;
+        }
+    }
+
+#pragma clang diagnostic pop
+#else
+
+    /* No ALPN support */
     if (alp != NULL) {
         *alp = NULL;
     }
 
+#endif
+
     if (retValue == errSSLWouldBlock) {
         msg_Dbg(crd, "handshake is blocked, try again later");
         return 1 + (sys->b_blocking_send ? 1 : 0);
@@ -646,11 +749,6 @@ error:
 static vlc_tls_t *st_ClientSessionOpen(vlc_tls_creds_t *crd, vlc_tls_t *sock,
                                  const char *hostname, const char *const *alpn)
 {
-    if (alpn != NULL) {
-        msg_Warn(crd, "Ignoring ALPN request due to lack of support in the backend. Proxy behavior potentially undefined.");
-#warning ALPN support missing, proxy behavior potentially undefined (rdar://29127318, #17721)
-    }
-
     msg_Dbg(crd, "open TLS session for %s", hostname);
 
     vlc_tls_t *tls = st_SessionOpenCommon(crd, sock, false);
@@ -665,6 +763,47 @@ static vlc_tls_t *st_ClientSessionOpen(vlc_tls_creds_t *crd, vlc_tls_t *sock,
         goto error;
     }
 
+// Only try to use ALPN on recent enough SDKs
+// macOS 10.13.2, iOS 11, tvOS 11, watchOS 4
+#if (TARGET_OS_OSX    && MAC_OS_X_VERSION_MAX_ALLOWED     >= 101302) || \
+    (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED  >= 110000) || \
+    (TARGET_OS_TV     && __TV_OS_VERSION_MAX_ALLOWED      >= 110000) || \
+    (TARGET_OS_WATCH  && __WATCH_OS_VERSION_MAX_ALLOWED   >= 40000)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+
+    /* Handle ALPN */
+    if (alpn != NULL) {
+        if (SSLSetALPNProtocols != NULL) {
+            CFMutableArrayRef alpnValues = alpnToCFArray(alpn);
+
+            if (alpnValues == NULL) {
+                msg_Err(crd, "cannot create CFMutableArray for ALPN values");
+                goto error;
+            }
+
+            OSStatus ret = SSLSetALPNProtocols(sys->p_context, alpnValues);
+            if (ret != noErr){
+                msg_Err(crd, "failed setting ALPN protocols (%i)", ret);
+            }
+            CFRelease(alpnValues);
+        } else {
+            msg_Warn(crd, "Ignoring ALPN request due to lack of support in the backend. Proxy behavior potentially undefined.");
+        }
+    }
+
+#pragma clang diagnostic pop
+#else
+
+    /* No ALPN support */
+    if (alpn != NULL) {
+        // Fallback if SDK does not has SSLSetALPNProtocols
+        msg_Warn(crd, "Compiled in SDK without ALPN support. Proxy behavior potentially undefined.");
+        #warning ALPN support in your SDK version missing (need 10.13.2), proxy behavior potentially undefined (rdar://29127318, #17721)
+    }
+
+#endif
+
     /* disable automatic validation. We do so manually to also handle invalid
        certificates */
 
-- 
2.14.3 (Apple Git-98)



More information about the vlc-devel mailing list