123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- From 64482a17f5ebbc0fa2945635d236047ebff7016f Mon Sep 17 00:00:00 2001
- From: David Fuhrmann <david.fuhrmann@googlemail.com>
- Date: Tue, 17 Dec 2013 23:03:06 +0100
- Subject: [PATCH 01/18] Add secure transport TLS module
- Secure Transport is a TLS library part of the Security framework
- (preinstalled on every iOS and MacOS device). This library does
- certificate validation during handshake automatically using the
- root certificates from the preinstalled certificate store.
- The main reason for this module is proper certificate validation
- on iOS devices. This is not possible with gnutls, because there is
- no access to the root certificates for external applications.
- The module is also intended for use on OSX.
- (manually backported from 673d45d0d54ac89bd553a8e35242426d14ba4212)
- ---
- modules/misc/Modules.am | 8 +
- modules/misc/securetransport.c | 596 +++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 604 insertions(+)
- create mode 100644 modules/misc/securetransport.c
- diff --git a/modules/misc/Modules.am b/modules/misc/Modules.am
- index 08fe043..97690e5 100644
- --- a/modules/misc/Modules.am
- +++ b/modules/misc/Modules.am
- @@ -30,6 +30,14 @@ endif
- EXTRA_LTLIBRARIES += libgnutls_plugin.la
- libvlc_LTLIBRARIES += $(LTLIBgnutls)
-
- +if HAVE_DARWIN
- +libsecuretransport_plugin_la_SOURCES = securetransport.c
- +libsecuretransport_plugin_la_CFLAGS = $(AM_CFLAGS) $(SECURETRANSPORT_CFLAGS)
- +libsecuretransport_plugin_la_LIBADD = $(SECURETRANSPORT_LIBS)
- +libsecuretransport_plugin_la_LDFLAGS = $(AM_LDFLAGS) -Wl,-framework,Security,-framework,CoreFoundation
- +libvlc_LTLIBRARIES += libsecuretransport_plugin.la
- +endif
- +
- libxdg_screensaver_plugin_la_SOURCES = inhibit/xdg.c
- libxdg_screensaver_plugin_la_CFLAGS = $(AM_CFLAGS)
- libxdg_screensaver_plugin_la_LIBADD = $(AM_LIBADD)
- diff --git a/modules/misc/securetransport.c b/modules/misc/securetransport.c
- new file mode 100644
- index 0000000..f0b12c8
- --- /dev/null
- +++ b/modules/misc/securetransport.c
- @@ -0,0 +1,596 @@
- +/*****************************************************************************
- + * securetransport.c
- + *****************************************************************************
- + * Copyright (C) 2013 David Fuhrmann
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU Lesser General Public License as published by
- + * the Free Software Foundation; either version 2.1 of the License, or
- + * (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU Lesser General Public License for more details.
- + *
- + * You should have received a copy of the GNU Öesser General Public License
- + * along with this program; if not, write to the Free Software Foundation,
- + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- + *****************************************************************************/
- +
- +/*****************************************************************************
- + * Preamble
- + *****************************************************************************/
- +
- +#ifdef HAVE_CONFIG_H
- +# include "config.h"
- +#endif
- +
- +#include <vlc_common.h>
- +#include <vlc_plugin.h>
- +#include <vlc_tls.h>
- +#include <vlc_dialog.h>
- +
- +#include <Security/Security.h>
- +#include <Security/SecureTransport.h>
- +#include <TargetConditionals.h>
- +
- +/* From MacErrors.h (cannot be included because it isn't present in iOS: */
- +#ifndef ioErr
- +# define ioErr -36
- +#endif
- +
- +/*****************************************************************************
- + * Module descriptor
- + *****************************************************************************/
- +static int OpenClient (vlc_tls_creds_t *);
- +static void CloseClient (vlc_tls_creds_t *);
- +
- +vlc_module_begin ()
- + set_description(N_("TLS support for OS X and iOS"))
- + set_capability("tls client", 2)
- + set_callbacks(OpenClient, CloseClient)
- + set_category(CAT_ADVANCED)
- + set_subcategory(SUBCAT_ADVANCED_NETWORK)
- +vlc_module_end ()
- +
- +
- +#define cfKeyHost CFSTR("host")
- +#define cfKeyCertificate CFSTR("certificate")
- +
- +struct vlc_tls_creds_sys
- +{
- + CFMutableArrayRef whitelist;
- +};
- +
- +struct vlc_tls_sys {
- + SSLContextRef p_context;
- + vlc_tls_creds_sys_t *p_cred;
- + size_t i_send_buffered_bytes;
- + int i_fd;
- +
- + bool b_blocking_send;
- + bool b_handshaked;
- +};
- +
- +static int st_Error (vlc_tls_t *obj, int val)
- +{
- + switch (val)
- + {
- + /* peer performed shutdown */
- + case errSSLClosedNoNotify:
- + case errSSLClosedGraceful:
- + msg_Dbg(obj, "Got shutdown notification");
- + return 0;
- +
- + case errSSLWouldBlock:
- + errno = EAGAIN;
- + break;
- +
- + default:
- + msg_Err (obj, "Found error %d", val);
- + errno = ECONNRESET;
- + }
- + return -1;
- +}
- +
- +/*
- + * Read function called by secure transport for socket read.
- + *
- + * Function is based on Apples SSLSample sample code.
- + */
- +static OSStatus st_SocketReadFunc (SSLConnectionRef connection,
- + void *data,
- + size_t *dataLength) {
- +
- + vlc_tls_t *session = (vlc_tls_t *)connection;
- + vlc_tls_sys_t *sys = session->sys;
- +
- + size_t bytesToGo = *dataLength;
- + size_t initLen = bytesToGo;
- + UInt8 *currData = (UInt8 *)data;
- + OSStatus retValue = noErr;
- + ssize_t val;
- +
- + for(;;) {
- + val = read(sys->i_fd, currData, bytesToGo);
- + if (val <= 0) {
- + if(val == 0) {
- + msg_Dbg(session, "found eof");
- + retValue = errSSLClosedGraceful;
- + } else { /* do the switch */
- + switch(errno) {
- + case ENOENT:
- + /* connection closed */
- + retValue = errSSLClosedGraceful;
- + break;
- + case ECONNRESET:
- + retValue = errSSLClosedAbort;
- + break;
- + case EAGAIN:
- + retValue = errSSLWouldBlock;
- + sys->b_blocking_send = false;
- + break;
- + default:
- + msg_Err(session, "try to read %d bytes, got error %d",
- + (int)bytesToGo, errno);
- + retValue = ioErr;
- + break;
- + }
- + }
- + break;
- + } else {
- + bytesToGo -= val;
- + currData += val;
- + }
- +
- + if(bytesToGo == 0) {
- + /* filled buffer with incoming data, done */
- + break;
- + }
- + }
- + *dataLength = initLen - bytesToGo;
- +
- + return retValue;
- +}
- +
- +/*
- + * Write function called by secure transport for socket read.
- + *
- + * Function is based on Apples SSLSample sample code.
- + */
- +static OSStatus st_SocketWriteFunc (SSLConnectionRef connection,
- + const void *data,
- + size_t *dataLength) {
- +
- + vlc_tls_t *session = (vlc_tls_t *)connection;
- + vlc_tls_sys_t *sys = session->sys;
- +
- + size_t bytesSent = 0;
- + size_t dataLen = *dataLength;
- + OSStatus retValue = noErr;
- + ssize_t val;
- +
- + do {
- + val = write(sys->i_fd, (char *)data + bytesSent, dataLen - bytesSent);
- + } while (val >= 0 && (bytesSent += val) < dataLen);
- +
- + if(val < 0) {
- + if(errno == EAGAIN) {
- + retValue = errSSLWouldBlock;
- + sys->b_blocking_send = true;
- + } else {
- + msg_Err(session, "error while writing: %d", errno);
- + retValue = ioErr;
- + }
- + }
- +
- + *dataLength = bytesSent;
- + return retValue;
- +}
- +
- +static int st_validateServerCertificate (vlc_tls_t *session, const char *hostname) {
- +
- + int result = -1;
- + vlc_tls_sys_t *sys = session->sys;
- + SecCertificateRef leaf_cert = NULL;
- +
- + SecTrustRef trust = NULL;
- + OSStatus ret = SSLCopyPeerTrust (sys->p_context, &trust);
- + if (ret != noErr || trust == NULL) {
- + msg_Err(session, "error getting certifictate chain");
- + return -1;
- + }
- +
- + CFStringRef cfHostname = CFStringCreateWithCString(kCFAllocatorDefault,
- + hostname,
- + kCFStringEncodingUTF8);
- +
- +
- + /* enable default root / anchor certificates */
- + ret = SecTrustSetAnchorCertificates (trust, NULL);
- + if (ret != noErr) {
- + msg_Err(session, "error setting anchor certificates");
- + result = -1;
- + goto out;
- + }
- +
- + SecTrustResultType trust_eval_result = 0;
- +
- + ret = SecTrustEvaluate(trust, &trust_eval_result);
- + if(ret != noErr) {
- + msg_Err(session, "error calling SecTrustEvaluate");
- + result = -1;
- + goto out;
- + }
- +
- + switch (trust_eval_result) {
- + case kSecTrustResultUnspecified:
- + case kSecTrustResultProceed:
- + msg_Dbg(session, "cerfificate verification successful, result is %d", trust_eval_result);
- + result = 0;
- + goto out;
- +
- + case kSecTrustResultRecoverableTrustFailure:
- + case kSecTrustResultDeny:
- + default:
- + msg_Warn(session, "cerfificate verification failed, result is %d", trust_eval_result);
- + }
- +
- + /* get leaf certificate */
- + /* SSLCopyPeerCertificates is only available on OSX 10.5 or later */
- +#if !TARGET_OS_IPHONE
- + CFArrayRef cert_chain = NULL;
- + ret = SSLCopyPeerCertificates (sys->p_context, &cert_chain);
- + if (ret != noErr || !cert_chain) {
- + result = -1;
- + goto out;
- + }
- +
- + if (CFArrayGetCount (cert_chain) == 0) {
- + CFRelease (cert_chain);
- + result = -1;
- + goto out;
- + }
- +
- + leaf_cert = (SecCertificateRef)CFArrayGetValueAtIndex (cert_chain, 0);
- + CFRetain (leaf_cert);
- + CFRelease (cert_chain);
- +#else
- + /* SecTrustGetCertificateAtIndex is only available on 10.7 or iOS */
- + if (SecTrustGetCertificateCount (trust) == 0) {
- + result = -1;
- + goto out;
- + }
- +
- + leaf_cert = SecTrustGetCertificateAtIndex (trust, 0);
- + CFRetain (leaf_cert);
- +#endif
- +
- +
- + /* check if leaf already accepted */
- + CFIndex max = CFArrayGetCount (sys->p_cred->whitelist);
- + for (CFIndex i = 0; i < max; ++i) {
- + CFDictionaryRef dict = CFArrayGetValueAtIndex (sys->p_cred->whitelist, i);
- + CFStringRef knownHost = (CFStringRef)CFDictionaryGetValue (dict, cfKeyHost);
- + SecCertificateRef knownCert = (SecCertificateRef)CFDictionaryGetValue (dict, cfKeyCertificate);
- +
- + if (!knownHost || !knownCert)
- + continue;
- +
- + if (CFEqual (knownHost, cfHostname) && CFEqual (knownCert, leaf_cert)) {
- + msg_Warn(session, "certificate already accepted, continuing");
- + result = 0;
- + goto out;
- + }
- + }
- +
- + /* We do not show more certificate details yet because there is no proper API to get
- + a summary of the certificate. SecCertificateCopySubjectSummary is the only method
- + available on iOS and 10.6. More promising API functions such as
- + SecCertificateCopyLongDescription also print out the subject only, more or less.
- + But only showing the certificate subject is of no real help for the user.
- + We could use SecCertificateCopyValues, but then we need to parse all OID values for
- + ourself. This is too mad for just printing information the user will never check
- + anyway.
- + */
- +
- + const char *msg = N_("You attempted to reach %s. "
- + "However the security certificate presented by the server "
- + "is unknown and could not be authenticated by any trusted "
- + "Certification Authority. "
- + "This problem may be caused by a configuration error "
- + "or an attempt to breach your security or your privacy.\n\n"
- + "If in doubt, abort now.\n");
- + int answer = dialog_Question (session, _("Insecure site"), vlc_gettext (msg),
- + _("Abort"), _("Accept certificate temporarily"), NULL, hostname);
- +
- + if(answer == 2) {
- + msg_Warn(session, "Proceeding despite of failed certificate validation");
- +
- + /* save leaf certificate in whitelist */
- + const void *keys[] = {cfKeyHost, cfKeyCertificate};
- + const void *values[] = {cfHostname, leaf_cert};
- + CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault,
- + keys, values, 2,
- + &kCFTypeDictionaryKeyCallBacks,
- + &kCFTypeDictionaryValueCallBacks);
- + if(!dict) {
- + msg_Err (session, "error creating dict");
- + result = -1;
- + goto out;
- + }
- +
- + CFArrayAppendValue (sys->p_cred->whitelist, dict);
- + CFRelease (dict);
- +
- + result = 0;
- + goto out;
- +
- + } else {
- + result = -1;
- + goto out;
- + }
- +
- +out:
- + CFRelease (trust);
- +
- + if (cfHostname)
- + CFRelease (cfHostname);
- + if (leaf_cert)
- + CFRelease (leaf_cert);
- +
- + return result;
- +}
- +
- +/*
- + * @return -1 on fatal error, 0 on successful handshake completion,
- + * 1 if more would-be blocking recv is needed,
- + * 2 if more would-be blocking send is required.
- + */
- +static int st_Handshake (vlc_tls_t *session, const char *host,
- + const char *service) {
- + VLC_UNUSED(service);
- +
- + vlc_tls_sys_t *sys = session->sys;
- +
- + OSStatus retValue = SSLHandshake(sys->p_context);
- +
- + if (retValue == errSSLWouldBlock) {
- + msg_Dbg(session, "handshake is blocked, try again later");
- + return 1 + (sys->b_blocking_send ? 1 : 0);
- + }
- +
- + switch (retValue) {
- + case noErr:
- + if(st_validateServerCertificate(session, host) != 0) {
- + return -1;
- + }
- + msg_Dbg(session, "handshake completed successfully");
- + sys->b_handshaked = true;
- + return 0;
- +
- + case errSSLServerAuthCompleted:
- + return st_Handshake (session, host, service);
- +
- + case errSSLConnectionRefused:
- + msg_Err(session, "connection was refused");
- + return -1;
- + case errSSLNegotiation:
- + msg_Err(session, "cipher suite negotiation failed");
- + return -1;
- + case errSSLFatalAlert:
- + msg_Err(session, "fatal error occured during handshake");
- + return -1;
- +
- + default:
- + msg_Err(session, "handshake returned error %d", (int)retValue);
- + return -1;
- + }
- +}
- +
- +/**
- + * Sends data through a TLS session.
- + */
- +static int st_Send (void *opaque, const void *buf, size_t length)
- +{
- + vlc_tls_t *session = opaque;
- + vlc_tls_sys_t *sys = session->sys;
- + OSStatus ret = noErr;
- +
- + /*
- + * SSLWrite does not return the number of bytes actually written to
- + * the socket, but the number of bytes written to the internal cache.
- + *
- + * If return value is errSSLWouldBlock, the underlying socket cannot
- + * send all data, but the data is already cached. In this situation,
- + * we need to call SSLWrite again. To ensure this call even for the
- + * last bytes, we return EAGAIN. On the next call, we give no new data
- + * to SSLWrite until the error is not errSSLWouldBlock anymore.
- + *
- + * This code is adapted the same way as done in curl.
- + * (https://github.com/bagder/curl/blob/master/lib/curl_darwinssl.c#L2067)
- + */
- +
- + size_t actualSize;
- + if (sys->i_send_buffered_bytes > 0) {
- + ret = SSLWrite(sys->p_context, NULL, 0, &actualSize);
- +
- + if (ret == noErr) {
- + /* actualSize remains zero because no new data send */
- + actualSize = sys->i_send_buffered_bytes;
- + sys->i_send_buffered_bytes = 0;
- +
- + } else if (ret == errSSLWouldBlock) {
- + /* EAGAIN is not expected by the core in this situation,
- + so use EINTR here */
- + errno = EINTR;
- + return -1;
- + }
- +
- + } else {
- + ret = SSLWrite(sys->p_context, buf, length, &actualSize);
- +
- + if (ret == errSSLWouldBlock) {
- + sys->i_send_buffered_bytes = length;
- + /* EAGAIN is not expected by the core in this situation,
- + so use EINTR here */
- + errno = EINTR;
- + return -1;
- + }
- + }
- +
- + return ret != noErr ? st_Error(session, ret) : actualSize;
- +}
- +
- +/**
- + * Receives data through a TLS session.
- + */
- +static int st_Recv (void *opaque, void *buf, size_t length)
- +{
- + vlc_tls_t *session = opaque;
- + vlc_tls_sys_t *sys = session->sys;
- +
- + size_t actualSize;
- + OSStatus ret = SSLRead(sys->p_context, buf, length, &actualSize);
- +
- + if(ret == errSSLWouldBlock && actualSize)
- + return actualSize;
- +
- + return ret != noErr ? st_Error(session, ret) : actualSize;
- +}
- +
- +/**
- + * Closes a client-side TLS credentials.
- + */
- +static void st_ClientSessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session) {
- +
- + VLC_UNUSED(crd);
- +
- + vlc_tls_sys_t *sys = session->sys;
- + msg_Dbg(session, "close TLS session");
- +
- + if(sys->b_handshaked) {
- + OSStatus ret = SSLClose(sys->p_context);
- + if(ret != noErr) {
- + msg_Err(session, "error closing ssl context");
- + }
- + }
- +
- + if (sys->p_context) {
- +#if TARGET_OS_IPHONE
- + CFRelease(sys->p_context);
- +#else
- + if(SSLDisposeContext(sys->p_context) != noErr) {
- + msg_Err(session, "error deleting context");
- + }
- +#endif
- + }
- + free (sys);
- +}
- +
- +/**
- + * Initializes a client-side TLS session.
- + */
- +static int st_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
- + int fd, const char *hostname) {
- + msg_Dbg(session, "open TLS session for %s", hostname);
- +
- + vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
- + if (unlikely(sys == NULL))
- + return VLC_ENOMEM;
- +
- + sys->p_cred = crd->sys;
- + sys->i_fd = fd;
- + sys->b_handshaked = false;
- + sys->b_blocking_send = false;
- + sys->i_send_buffered_bytes = 0;
- +
- + session->sys = sys;
- + session->sock.p_sys = session;
- + session->sock.pf_send = st_Send;
- + session->sock.pf_recv = st_Recv;
- + session->handshake = st_Handshake;
- +
- + SSLContextRef p_context = NULL;
- +#if TARGET_OS_IPHONE
- + p_context = SSLCreateContext (NULL, kSSLClientSide, kSSLStreamType);
- + if(p_context == NULL) {
- + msg_Err(session, "cannot create ssl context");
- + goto error;
- + }
- +#else
- + if (SSLNewContext (false, &p_context) != noErr) {
- + msg_Err(session, "error calling SSLNewContext");
- + goto error;
- + }
- +#endif
- +
- + sys->p_context = p_context;
- +
- + OSStatus ret = SSLSetIOFuncs (p_context, st_SocketReadFunc, st_SocketWriteFunc);
- + if(ret != noErr) {
- + msg_Err(session, "cannot set io functions");
- + goto error;
- + }
- +
- + ret = SSLSetConnection (p_context, session);
- + if(ret != noErr) {
- + msg_Err(session, "cannot set connection");
- + goto error;
- + }
- +
- + ret = SSLSetPeerDomainName (p_context, hostname, strlen(hostname));
- + if(ret != noErr) {
- + msg_Err(session, "cannot set peer domain name");
- + goto error;
- + }
- +
- + /* disable automatic validation. We do so manually to also handle invalid
- + certificates */
- +
- + /* this has effect only on iOS 5 and OSX 10.8 or later ... */
- + SSLSetSessionOption (sys->p_context, kSSLSessionOptionBreakOnServerAuth, true);
- +#if !TARGET_OS_IPHONE
- + /* ... thus calling this for earlier osx versions, which is not available on iOS in turn */
- + SSLSetEnableCertVerify (sys->p_context, false);
- +#endif
- +
- + return VLC_SUCCESS;
- +
- +error:
- + st_ClientSessionClose(crd, session);
- + return VLC_EGENERIC;
- +}
- +
- +/**
- + * Initializes a client-side TLS credentials.
- + */
- +static int OpenClient (vlc_tls_creds_t *crd) {
- +
- + msg_Dbg(crd, "open st client");
- +
- + vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
- + if (unlikely(sys == NULL))
- + return VLC_ENOMEM;
- +
- + sys->whitelist = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- +
- + crd->sys = sys;
- + crd->open = st_ClientSessionOpen;
- + crd->close = st_ClientSessionClose;
- +
- + return VLC_SUCCESS;
- +
- +}
- +
- +static void CloseClient (vlc_tls_creds_t *crd) {
- + msg_Dbg(crd, "close secure transport client");
- +
- + vlc_tls_creds_sys_t *sys = crd->sys;
- +
- + if (sys->whitelist)
- + CFRelease(sys->whitelist);
- +
- + free (sys);
- +}
- --
- 1.8.3.4 (Apple Git-47)
|