Browse code

lavf/tls: Support Secure Transport

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Rodger Combs authored on 2015/05/29 04:46:16
Showing 8 changed files
... ...
@@ -23,6 +23,7 @@ version <next>:
23 23
 - VP9 high bit-depth and extended colorspaces decoding support
24 24
 - WebPAnimEncoder API when available for encoding and muxing WebP
25 25
 - Direct3D11-accelerated decoding
26
+- Support Secure Transport
26 27
 
27 28
 
28 29
 version 2.6:
... ...
@@ -276,6 +276,8 @@ External library support:
276 276
   --enable-openssl         enable openssl, needed for https support
277 277
                            if gnutls is not used [no]
278 278
   --disable-sdl            disable sdl [autodetect]
279
+  --disable-securetransport disable Secure Transport, needed for TLS support
280
+                           on OSX if openssl and gnutls are not used [autodetect]
279 281
   --enable-x11grab         enable X11 grabbing (legacy) [no]
280 282
   --disable-xlib           disable xlib [autodetect]
281 283
   --disable-zlib           disable zlib [autodetect]
... ...
@@ -1424,6 +1426,7 @@ EXTERNAL_LIBRARY_LIST="
1424 1424
     opengl
1425 1425
     openssl
1426 1426
     sdl
1427
+    securetransport
1427 1428
     x11grab
1428 1429
     xlib
1429 1430
     zlib
... ...
@@ -2636,9 +2639,10 @@ sctp_protocol_deps="struct_sctp_event_subscribe"
2636 2636
 sctp_protocol_select="network"
2637 2637
 srtp_protocol_select="rtp_protocol"
2638 2638
 tcp_protocol_select="network"
2639
-tls_gnutls_protocol_deps="gnutls"
2640
-tls_openssl_protocol_deps="openssl !tls_gnutls_protocol"
2641
-tls_protocol_deps_any="tls_gnutls_protocol tls_openssl_protocol"
2639
+tls_securetransport_protocol_deps="securetransport"
2640
+tls_gnutls_protocol_deps="gnutls !tls_securetransport_protocol"
2641
+tls_openssl_protocol_deps="openssl !tls_securetransport_protocol !tls_gnutls_protocol"
2642
+tls_protocol_deps_any="tls_securetransport_protocol tls_gnutls_protocol tls_openssl_protocol"
2642 2643
 tls_protocol_select="tcp_protocol"
2643 2644
 udp_protocol_select="network"
2644 2645
 udplite_protocol_select="network"
... ...
@@ -5213,6 +5217,10 @@ if ! disabled sdl; then
5213 5213
 fi
5214 5214
 enabled sdl && add_cflags $sdl_cflags && add_extralibs $sdl_libs
5215 5215
 
5216
+disabled securetransport || { check_func SecIdentityCreate "-Wl,-framework,CoreFoundation -Wl,-framework,Security" &&
5217
+    check_lib2 Security/SecureTransport.h SSLCreateContext "-Wl,-framework,CoreFoundation -Wl,-framework,Security" &&
5218
+    enable securetransport; }
5219
+
5216 5220
 makeinfo --version > /dev/null 2>&1 && enable makeinfo  || disable makeinfo
5217 5221
 enabled makeinfo && (makeinfo --version | \
5218 5222
                      grep -q 'makeinfo (GNU texinfo) 5' > /dev/null 2>&1) \
... ...
@@ -523,6 +523,7 @@ OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
523 523
 OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
524 524
 OBJS-$(CONFIG_TLS_GNUTLS_PROTOCOL)       += tls_gnutls.o tls.o
525 525
 OBJS-$(CONFIG_TLS_OPENSSL_PROTOCOL)      += tls_openssl.o tls.o
526
+OBJS-$(CONFIG_TLS_SECURETRANSPORT_PROTOCOL) += tls_securetransport.o tls.o
526 527
 OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o
527 528
 OBJS-$(CONFIG_UDPLITE_PROTOCOL)          += udp.o
528 529
 OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o
... ...
@@ -377,6 +377,7 @@ void av_register_all(void)
377 377
     REGISTER_PROTOCOL(SRTP,             srtp);
378 378
     REGISTER_PROTOCOL(SUBFILE,          subfile);
379 379
     REGISTER_PROTOCOL(TCP,              tcp);
380
+    REGISTER_PROTOCOL(TLS_SECURETRANSPORT, tls_securetransport);
380 381
     REGISTER_PROTOCOL(TLS_GNUTLS,       tls_gnutls);
381 382
     REGISTER_PROTOCOL(TLS_OPENSSL,      tls_openssl);
382 383
     REGISTER_PROTOCOL(UDP,              udp);
... ...
@@ -263,7 +263,9 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags,
263 263
 
264 264
     *puc = NULL;
265 265
     if (av_strstart(filename, "https:", NULL))
266
-        av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile with openssl or gnutls enabled.\n");
266
+        av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile with "
267
+                                     "openssl, gnutls,\n"
268
+                                     "or securetransport enabled.\n");
267 269
     return AVERROR_PROTOCOL_NOT_FOUND;
268 270
 }
269 271
 
... ...
@@ -26,7 +26,7 @@
26 26
 #include "url.h"
27 27
 #include "libavutil/opt.h"
28 28
 
29
-#define CONFIG_TLS_PROTOCOL (CONFIG_TLS_GNUTLS_PROTOCOL | CONFIG_TLS_OPENSSL_PROTOCOL)
29
+#define CONFIG_TLS_PROTOCOL (CONFIG_TLS_GNUTLS_PROTOCOL | CONFIG_TLS_OPENSSL_PROTOCOL | CONFIG_TLS_SECURETRANSPORT_PROTOCOL)
30 30
 
31 31
 typedef struct TLSShared {
32 32
     char *ca_file;
33 33
new file mode 100644
... ...
@@ -0,0 +1,393 @@
0
+/*
1
+ * This file is part of FFmpeg.
2
+ *
3
+ * FFmpeg is free software; you can redistribute it and/or
4
+ * modify it under the terms of the GNU Lesser General Public License
5
+ * as published by the Free Software Foundation; either
6
+ * version 2.1 of the License, or (at your option) any later version.
7
+ *
8
+ * FFmpeg is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+ * GNU Lesser General Public License for more details.
12
+ *
13
+ * You should have received a copy of the GNU Lesser General Public License
14
+ * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
15
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+ */
17
+
18
+#include <errno.h>
19
+
20
+
21
+#include "avformat.h"
22
+#include "internal.h"
23
+#include "network.h"
24
+#include "os_support.h"
25
+#include "url.h"
26
+#include "tls.h"
27
+#include "libavcodec/internal.h"
28
+#include "libavutil/avstring.h"
29
+#include "libavutil/opt.h"
30
+#include "libavutil/parseutils.h"
31
+
32
+#include <Security/Security.h>
33
+#include <Security/SecureTransport.h>
34
+#include <CoreFoundation/CoreFoundation.h>
35
+
36
+// We use a private API call here; it's good enough for WebKit.
37
+SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
38
+#define ioErr -36
39
+
40
+typedef struct TLSContext {
41
+    const AVClass *class;
42
+    TLSShared tls_shared;
43
+    SSLContextRef ssl_context;
44
+    CFArrayRef ca_array;
45
+    int lastErr;
46
+} TLSContext;
47
+
48
+static int print_tls_error(URLContext *h, int ret)
49
+{
50
+    TLSContext *c = h->priv_data;
51
+    switch (ret) {
52
+    case errSSLWouldBlock:
53
+        break;
54
+    case errSSLXCertChainInvalid:
55
+        av_log(h, AV_LOG_ERROR, "Invalid certificate chain\n");
56
+        return AVERROR(EIO);
57
+    case ioErr:
58
+        return c->lastErr;
59
+    default:
60
+        av_log(h, AV_LOG_ERROR, "IO Error: %i\n", ret);
61
+        return AVERROR(EIO);
62
+    }
63
+    return AVERROR(EIO);
64
+}
65
+
66
+static int import_pem(URLContext *h, char *path, CFArrayRef *array)
67
+{
68
+    AVIOContext *s = NULL;
69
+    CFDataRef data = NULL;
70
+    int64_t ret = 0;
71
+    char *buf = NULL;
72
+    SecExternalFormat format = kSecFormatPEMSequence;
73
+    SecExternalFormat type = kSecItemTypeAggregate;
74
+    CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
75
+    if (!pathStr) {
76
+        ret = AVERROR(ENOMEM);
77
+        goto end;
78
+    }
79
+
80
+    if ((ret = avio_open2(&s, path, AVIO_FLAG_READ,
81
+                          &h->interrupt_callback, NULL)) < 0)
82
+        goto end;
83
+
84
+    if ((ret = avio_size(s)) < 0)
85
+        goto end;
86
+
87
+    if (ret == 0) {
88
+        ret = AVERROR_INVALIDDATA;
89
+        goto end;
90
+    }
91
+
92
+    if (!(buf = av_malloc(ret))) {
93
+        ret = AVERROR(ENOMEM);
94
+        goto end;
95
+    }
96
+
97
+    if ((ret = avio_read(s, buf, ret)) < 0)
98
+        goto end;
99
+
100
+    data = CFDataCreate(kCFAllocatorDefault, buf, ret);
101
+
102
+    if (SecItemImport(data, pathStr, &format, &type,
103
+                      0, NULL, NULL, array) != noErr || !array) {
104
+        ret = AVERROR_UNKNOWN;
105
+        goto end;
106
+    }
107
+
108
+    if (CFArrayGetCount(*array) == 0) {
109
+        ret = AVERROR_INVALIDDATA;
110
+        goto end;
111
+    }
112
+
113
+end:
114
+    av_free(buf);
115
+    if (pathStr)
116
+        CFRelease(pathStr);
117
+    if (data)
118
+        CFRelease(data);
119
+    if (s)
120
+        avio_close(s);
121
+    return ret;
122
+}
123
+
124
+static int load_ca(URLContext *h)
125
+{
126
+    TLSContext *c = h->priv_data;
127
+    int ret = 0;
128
+    CFArrayRef array = NULL;
129
+
130
+    if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
131
+        goto end;
132
+
133
+    if (!(c->ca_array = CFRetain(array))) {
134
+        ret = AVERROR(ENOMEM);
135
+        goto end;
136
+    }
137
+
138
+end:
139
+    if (array)
140
+        CFRelease(array);
141
+    return ret;
142
+}
143
+
144
+static int load_cert(URLContext *h)
145
+{
146
+    TLSContext *c = h->priv_data;
147
+    int ret = 0;
148
+    CFArrayRef certArray = NULL;
149
+    CFArrayRef keyArray = NULL;
150
+    SecIdentityRef id = NULL;
151
+    CFMutableArrayRef outArray = NULL;
152
+
153
+    if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
154
+        goto end;
155
+
156
+    if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
157
+        goto end;
158
+
159
+    if (!(id = SecIdentityCreate(kCFAllocatorDefault,
160
+                                 (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
161
+                                 (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
162
+        ret = AVERROR_UNKNOWN;
163
+        goto end;
164
+    }
165
+
166
+    if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
167
+        ret = AVERROR(ENOMEM);
168
+        goto end;
169
+    }
170
+
171
+    CFArraySetValueAtIndex(outArray, 0, id);
172
+
173
+    SSLSetCertificate(c->ssl_context, outArray);
174
+
175
+end:
176
+    if (certArray)
177
+        CFRelease(certArray);
178
+    if (keyArray)
179
+        CFRelease(keyArray);
180
+    if (outArray)
181
+        CFRelease(outArray);
182
+    if (id)
183
+        CFRelease(id);
184
+    return ret;
185
+}
186
+
187
+static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
188
+{
189
+    URLContext *h = (URLContext*)connection;
190
+    TLSContext *c = h->priv_data;
191
+    int read = ffurl_read_complete(c->tls_shared.tcp, data, *dataLength);
192
+    if (read <= 0) {
193
+        *dataLength = 0;
194
+        switch(AVUNERROR(read)) {
195
+            case ENOENT:
196
+            case 0:
197
+                return errSSLClosedGraceful;
198
+            case ECONNRESET:
199
+                return errSSLClosedAbort;
200
+            case EAGAIN:
201
+                return errSSLWouldBlock;
202
+            default:
203
+                c->lastErr = read;
204
+                return ioErr;
205
+        }
206
+    } else {
207
+        *dataLength = read;
208
+        return noErr;
209
+    }
210
+}
211
+
212
+static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
213
+{
214
+    URLContext *h = (URLContext*)connection;
215
+    TLSContext *c = h->priv_data;
216
+    int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
217
+    if (written <= 0) {
218
+        *dataLength = 0;
219
+        switch(AVUNERROR(written)) {
220
+            case EAGAIN:
221
+                return errSSLWouldBlock;
222
+            default:
223
+                c->lastErr = written;
224
+                return ioErr;
225
+        }
226
+    } else {
227
+        *dataLength = written;
228
+        return noErr;
229
+    }
230
+}
231
+
232
+static int tls_close(URLContext *h)
233
+{
234
+    TLSContext *c = h->priv_data;
235
+    if (c->ssl_context) {
236
+        SSLClose(c->ssl_context);
237
+        CFRelease(c->ssl_context);
238
+    }
239
+    if (c->ca_array)
240
+        CFRelease(c->ca_array);
241
+    if (c->tls_shared.tcp)
242
+        ffurl_close(c->tls_shared.tcp);
243
+    return 0;
244
+}
245
+
246
+#define CHECK_ERROR(func, ...) do {                                     \
247
+        OSStatus status = func(__VA_ARGS__);                            \
248
+        if (status != noErr) {                                          \
249
+            ret = AVERROR_UNKNOWN;                                      \
250
+            av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
251
+            goto fail;                                                  \
252
+        }                                                               \
253
+    } while (0)
254
+
255
+static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
256
+{
257
+    TLSContext *c = h->priv_data;
258
+    TLSShared *s = &c->tls_shared;
259
+    int ret;
260
+
261
+    if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
262
+        goto fail;
263
+
264
+    c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
265
+    if (!c->ssl_context) {
266
+        av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
267
+        ret = AVERROR(ENOMEM);
268
+        goto fail;
269
+    }
270
+    if (s->ca_file) {
271
+        if ((ret = load_ca(h)) < 0)
272
+            goto fail;
273
+        CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
274
+    }
275
+    if (s->cert_file)
276
+        if ((ret = load_cert(h)) < 0)
277
+            goto fail;
278
+    if (s->verify)
279
+        CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
280
+    CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
281
+    CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
282
+    while (1) {
283
+        OSStatus status = SSLHandshake(c->ssl_context);
284
+        if (status == errSSLServerAuthCompleted) {
285
+            SecTrustRef peerTrust;
286
+            SecTrustResultType trustResult;
287
+            if (!s->verify)
288
+                continue;
289
+
290
+            if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
291
+                ret = AVERROR(ENOMEM);
292
+                goto fail;
293
+            }
294
+
295
+            if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
296
+                ret = AVERROR_UNKNOWN;
297
+                goto fail;
298
+            }
299
+
300
+            if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
301
+                ret = AVERROR_UNKNOWN;
302
+                goto fail;
303
+            }
304
+
305
+            if (trustResult == kSecTrustResultProceed ||
306
+                trustResult == kSecTrustResultUnspecified) {
307
+                // certificate is trusted
308
+                status = errSSLWouldBlock; // so we call SSLHandshake again
309
+            } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
310
+                // not trusted, for some reason other than being expired
311
+                status = errSSLXCertChainInvalid;
312
+            } else {
313
+                // cannot use this certificate (fatal)
314
+                status = errSSLBadCert;
315
+            }
316
+
317
+            if (peerTrust)
318
+                CFRelease(peerTrust);
319
+        }
320
+        if (status == noErr)
321
+            break;
322
+
323
+        av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
324
+        ret = AVERROR(EIO);
325
+        goto fail;
326
+    }
327
+
328
+    return 0;
329
+fail:
330
+    tls_close(h);
331
+    return ret;
332
+}
333
+
334
+static int map_ssl_error(OSStatus status, size_t processed)
335
+{
336
+    switch (status) {
337
+    case noErr:
338
+        return processed;
339
+    case errSSLClosedGraceful:
340
+    case errSSLClosedNoNotify:
341
+        return 0;
342
+    default:
343
+        return (int)status;
344
+    }
345
+}
346
+
347
+static int tls_read(URLContext *h, uint8_t *buf, int size)
348
+{
349
+    TLSContext *c = h->priv_data;
350
+    size_t processed;
351
+    int ret = map_ssl_error(SSLRead(c->ssl_context, buf, size, &processed), processed);
352
+    if (ret > 0)
353
+        return ret;
354
+    if (ret == 0)
355
+        return AVERROR_EOF;
356
+    return print_tls_error(h, ret);
357
+}
358
+
359
+static int tls_write(URLContext *h, const uint8_t *buf, int size)
360
+{
361
+    TLSContext *c = h->priv_data;
362
+    size_t processed;
363
+    int ret = map_ssl_error(SSLWrite(c->ssl_context, buf, size, &processed), processed);
364
+    if (ret > 0)
365
+        return ret;
366
+    if (ret == 0)
367
+        return AVERROR_EOF;
368
+    return print_tls_error(h, ret);
369
+}
370
+
371
+static const AVOption options[] = {
372
+    TLS_COMMON_OPTIONS(TLSContext, tls_shared),
373
+    { NULL }
374
+};
375
+
376
+static const AVClass tls_class = {
377
+    .class_name = "tls",
378
+    .item_name  = av_default_item_name,
379
+    .option     = options,
380
+    .version    = LIBAVUTIL_VERSION_INT,
381
+};
382
+
383
+URLProtocol ff_tls_securetransport_protocol = {
384
+    .name           = "tls",
385
+    .url_open2      = tls_open,
386
+    .url_read       = tls_read,
387
+    .url_write      = tls_write,
388
+    .url_close      = tls_close,
389
+    .priv_data_size = sizeof(TLSContext),
390
+    .flags          = URL_PROTOCOL_FLAG_NETWORK,
391
+    .priv_data_class = &tls_class,
392
+};
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 56
33
-#define LIBAVFORMAT_VERSION_MINOR  33
34
-#define LIBAVFORMAT_VERSION_MICRO 101
33
+#define LIBAVFORMAT_VERSION_MINOR  34
34
+#define LIBAVFORMAT_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
37 37
                                                LIBAVFORMAT_VERSION_MINOR, \