]> git.sesse.net Git - ffmpeg/blob - libavformat/tls_securetransport.c
Merge commit '97c9a5084479eeb66f4beb100cc7589a2c8bfe81'
[ffmpeg] / libavformat / tls_securetransport.c
1 /*
2  * Copyright (c) 2015 Rodger Combs
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include <errno.h>
22
23
24 #include "avformat.h"
25 #include "avio_internal.h"
26 #include "internal.h"
27 #include "network.h"
28 #include "os_support.h"
29 #include "url.h"
30 #include "tls.h"
31 #include "libavcodec/internal.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/opt.h"
34 #include "libavutil/parseutils.h"
35
36 #include <Security/Security.h>
37 #include <Security/SecureTransport.h>
38 #include <CoreFoundation/CoreFoundation.h>
39
40 // We use a private API call here; it's good enough for WebKit.
41 SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
42 #define ioErr -36
43
44 typedef struct TLSContext {
45     const AVClass *class;
46     TLSShared tls_shared;
47     SSLContextRef ssl_context;
48     CFArrayRef ca_array;
49     int lastErr;
50 } TLSContext;
51
52 static int print_tls_error(URLContext *h, int ret)
53 {
54     TLSContext *c = h->priv_data;
55     switch (ret) {
56     case errSSLWouldBlock:
57         return AVERROR(EAGAIN);
58     case errSSLXCertChainInvalid:
59         av_log(h, AV_LOG_ERROR, "Invalid certificate chain\n");
60         return AVERROR(EIO);
61     case ioErr:
62         return c->lastErr;
63     default:
64         av_log(h, AV_LOG_ERROR, "IO Error: %i\n", ret);
65         return AVERROR(EIO);
66     }
67     return AVERROR(EIO);
68 }
69
70 static int import_pem(URLContext *h, char *path, CFArrayRef *array)
71 {
72 #if !HAVE_SECITEMIMPORT
73     return AVERROR_PATCHWELCOME;
74 #else
75     AVIOContext *s = NULL;
76     CFDataRef data = NULL;
77     int64_t ret = 0;
78     char *buf = NULL;
79     SecExternalFormat format = kSecFormatPEMSequence;
80     SecExternalFormat type = kSecItemTypeAggregate;
81     CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
82     if (!pathStr) {
83         ret = AVERROR(ENOMEM);
84         goto end;
85     }
86
87     if ((ret = ffio_open_whitelist(&s, path, AVIO_FLAG_READ,
88                                    &h->interrupt_callback, NULL,
89                                    h->protocol_whitelist, h->protocol_blacklist)) < 0)
90         goto end;
91
92     if ((ret = avio_size(s)) < 0)
93         goto end;
94
95     if (ret == 0) {
96         ret = AVERROR_INVALIDDATA;
97         goto end;
98     }
99
100     if (!(buf = av_malloc(ret))) {
101         ret = AVERROR(ENOMEM);
102         goto end;
103     }
104
105     if ((ret = avio_read(s, buf, ret)) < 0)
106         goto end;
107
108     data = CFDataCreate(kCFAllocatorDefault, buf, ret);
109
110     if (SecItemImport(data, pathStr, &format, &type,
111                       0, NULL, NULL, array) != noErr || !array) {
112         ret = AVERROR_UNKNOWN;
113         goto end;
114     }
115
116     if (CFArrayGetCount(*array) == 0) {
117         ret = AVERROR_INVALIDDATA;
118         goto end;
119     }
120
121 end:
122     av_free(buf);
123     if (pathStr)
124         CFRelease(pathStr);
125     if (data)
126         CFRelease(data);
127     if (s)
128         avio_close(s);
129     return ret;
130 #endif
131 }
132
133 static int load_ca(URLContext *h)
134 {
135     TLSContext *c = h->priv_data;
136     int ret = 0;
137     CFArrayRef array = NULL;
138
139     if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
140         goto end;
141
142     if (!(c->ca_array = CFRetain(array))) {
143         ret = AVERROR(ENOMEM);
144         goto end;
145     }
146
147 end:
148     if (array)
149         CFRelease(array);
150     return ret;
151 }
152
153 static int load_cert(URLContext *h)
154 {
155     TLSContext *c = h->priv_data;
156     int ret = 0;
157     CFArrayRef certArray = NULL;
158     CFArrayRef keyArray = NULL;
159     SecIdentityRef id = NULL;
160     CFMutableArrayRef outArray = NULL;
161
162     if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
163         goto end;
164
165     if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
166         goto end;
167
168     if (!(id = SecIdentityCreate(kCFAllocatorDefault,
169                                  (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
170                                  (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
171         ret = AVERROR_UNKNOWN;
172         goto end;
173     }
174
175     if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
176         ret = AVERROR(ENOMEM);
177         goto end;
178     }
179
180     CFArraySetValueAtIndex(outArray, 0, id);
181
182     SSLSetCertificate(c->ssl_context, outArray);
183
184 end:
185     if (certArray)
186         CFRelease(certArray);
187     if (keyArray)
188         CFRelease(keyArray);
189     if (outArray)
190         CFRelease(outArray);
191     if (id)
192         CFRelease(id);
193     return ret;
194 }
195
196 static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
197 {
198     URLContext *h = (URLContext*)connection;
199     TLSContext *c = h->priv_data;
200     size_t requested = *dataLength;
201     int read = ffurl_read(c->tls_shared.tcp, data, requested);
202     if (read <= 0) {
203         *dataLength = 0;
204         switch(AVUNERROR(read)) {
205             case ENOENT:
206             case 0:
207                 return errSSLClosedGraceful;
208             case ECONNRESET:
209                 return errSSLClosedAbort;
210             case EAGAIN:
211                 return errSSLWouldBlock;
212             default:
213                 c->lastErr = read;
214                 return ioErr;
215         }
216     } else {
217         *dataLength = read;
218         if (read < requested)
219             return errSSLWouldBlock;
220         else
221             return noErr;
222     }
223 }
224
225 static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
226 {
227     URLContext *h = (URLContext*)connection;
228     TLSContext *c = h->priv_data;
229     int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
230     if (written <= 0) {
231         *dataLength = 0;
232         switch(AVUNERROR(written)) {
233             case EAGAIN:
234                 return errSSLWouldBlock;
235             default:
236                 c->lastErr = written;
237                 return ioErr;
238         }
239     } else {
240         *dataLength = written;
241         return noErr;
242     }
243 }
244
245 static int tls_close(URLContext *h)
246 {
247     TLSContext *c = h->priv_data;
248     if (c->ssl_context) {
249         SSLClose(c->ssl_context);
250         CFRelease(c->ssl_context);
251     }
252     if (c->ca_array)
253         CFRelease(c->ca_array);
254     if (c->tls_shared.tcp)
255         ffurl_close(c->tls_shared.tcp);
256     return 0;
257 }
258
259 #define CHECK_ERROR(func, ...) do {                                     \
260         OSStatus status = func(__VA_ARGS__);                            \
261         if (status != noErr) {                                          \
262             ret = AVERROR_UNKNOWN;                                      \
263             av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
264             goto fail;                                                  \
265         }                                                               \
266     } while (0)
267
268 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
269 {
270     TLSContext *c = h->priv_data;
271     TLSShared *s = &c->tls_shared;
272     int ret;
273
274     if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
275         goto fail;
276
277     c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
278     if (!c->ssl_context) {
279         av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
280         ret = AVERROR(ENOMEM);
281         goto fail;
282     }
283     if (s->ca_file) {
284         if ((ret = load_ca(h)) < 0)
285             goto fail;
286     }
287     if (s->ca_file || !s->verify)
288         CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
289     if (s->cert_file)
290         if ((ret = load_cert(h)) < 0)
291             goto fail;
292     CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
293     CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
294     CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
295     while (1) {
296         OSStatus status = SSLHandshake(c->ssl_context);
297         if (status == errSSLServerAuthCompleted) {
298             SecTrustRef peerTrust;
299             SecTrustResultType trustResult;
300             if (!s->verify)
301                 continue;
302
303             if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
304                 ret = AVERROR(ENOMEM);
305                 goto fail;
306             }
307
308             if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
309                 ret = AVERROR_UNKNOWN;
310                 goto fail;
311             }
312
313             if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
314                 ret = AVERROR_UNKNOWN;
315                 goto fail;
316             }
317
318             if (trustResult == kSecTrustResultProceed ||
319                 trustResult == kSecTrustResultUnspecified) {
320                 // certificate is trusted
321                 status = errSSLWouldBlock; // so we call SSLHandshake again
322             } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
323                 // not trusted, for some reason other than being expired
324                 status = errSSLXCertChainInvalid;
325             } else {
326                 // cannot use this certificate (fatal)
327                 status = errSSLBadCert;
328             }
329
330             if (peerTrust)
331                 CFRelease(peerTrust);
332         }
333         if (status == noErr) {
334             break;
335         } else if (status != errSSLWouldBlock) {
336             av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
337             ret = AVERROR(EIO);
338             goto fail;
339         }
340     }
341
342     return 0;
343 fail:
344     tls_close(h);
345     return ret;
346 }
347
348 static int map_ssl_error(OSStatus status, size_t processed)
349 {
350     switch (status) {
351     case noErr:
352         return processed;
353     case errSSLClosedGraceful:
354     case errSSLClosedNoNotify:
355         return 0;
356     case errSSLWouldBlock:
357         if (processed > 0)
358             return processed;
359     default:
360         return (int)status;
361     }
362 }
363
364 static int tls_read(URLContext *h, uint8_t *buf, int size)
365 {
366     TLSContext *c = h->priv_data;
367     size_t available = 0, processed = 0;
368     int ret;
369     SSLGetBufferedReadSize(c->ssl_context, &available);
370     if (available)
371         size = FFMIN(available, size);
372     ret = SSLRead(c->ssl_context, buf, size, &processed);
373     ret = map_ssl_error(ret, processed);
374     if (ret > 0)
375         return ret;
376     if (ret == 0)
377         return AVERROR_EOF;
378     return print_tls_error(h, ret);
379 }
380
381 static int tls_write(URLContext *h, const uint8_t *buf, int size)
382 {
383     TLSContext *c = h->priv_data;
384     size_t processed = 0;
385     int ret = SSLWrite(c->ssl_context, buf, size, &processed);
386     ret = map_ssl_error(ret, processed);
387     if (ret > 0)
388         return ret;
389     if (ret == 0)
390         return AVERROR_EOF;
391     return print_tls_error(h, ret);
392 }
393
394 static int tls_get_file_handle(URLContext *h)
395 {
396     TLSContext *c = h->priv_data;
397     return ffurl_get_file_handle(c->tls_shared.tcp);
398 }
399
400 static const AVOption options[] = {
401     TLS_COMMON_OPTIONS(TLSContext, tls_shared),
402     { NULL }
403 };
404
405 static const AVClass tls_class = {
406     .class_name = "tls",
407     .item_name  = av_default_item_name,
408     .option     = options,
409     .version    = LIBAVUTIL_VERSION_INT,
410 };
411
412 const URLProtocol ff_tls_protocol = {
413     .name           = "tls",
414     .url_open2      = tls_open,
415     .url_read       = tls_read,
416     .url_write      = tls_write,
417     .url_close      = tls_close,
418     .url_get_file_handle = tls_get_file_handle,
419     .priv_data_size = sizeof(TLSContext),
420     .flags          = URL_PROTOCOL_FLAG_NETWORK,
421     .priv_data_class = &tls_class,
422 };