avformat/tls_securetransport: Add missing include
[ffmpeg.git] / 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         break;
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     AVIOContext *s = NULL;
73     CFDataRef data = NULL;
74     int64_t ret = 0;
75     char *buf = NULL;
76     SecExternalFormat format = kSecFormatPEMSequence;
77     SecExternalFormat type = kSecItemTypeAggregate;
78     CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
79     if (!pathStr) {
80         ret = AVERROR(ENOMEM);
81         goto end;
82     }
83
84     if ((ret = ffio_open_whitelist(&s, path, AVIO_FLAG_READ,
85                                    &h->interrupt_callback, NULL,
86                                    h->protocol_whitelist)) < 0)
87         goto end;
88
89     if ((ret = avio_size(s)) < 0)
90         goto end;
91
92     if (ret == 0) {
93         ret = AVERROR_INVALIDDATA;
94         goto end;
95     }
96
97     if (!(buf = av_malloc(ret))) {
98         ret = AVERROR(ENOMEM);
99         goto end;
100     }
101
102     if ((ret = avio_read(s, buf, ret)) < 0)
103         goto end;
104
105     data = CFDataCreate(kCFAllocatorDefault, buf, ret);
106
107     if (SecItemImport(data, pathStr, &format, &type,
108                       0, NULL, NULL, array) != noErr || !array) {
109         ret = AVERROR_UNKNOWN;
110         goto end;
111     }
112
113     if (CFArrayGetCount(*array) == 0) {
114         ret = AVERROR_INVALIDDATA;
115         goto end;
116     }
117
118 end:
119     av_free(buf);
120     if (pathStr)
121         CFRelease(pathStr);
122     if (data)
123         CFRelease(data);
124     if (s)
125         avio_close(s);
126     return ret;
127 }
128
129 static int load_ca(URLContext *h)
130 {
131     TLSContext *c = h->priv_data;
132     int ret = 0;
133     CFArrayRef array = NULL;
134
135     if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
136         goto end;
137
138     if (!(c->ca_array = CFRetain(array))) {
139         ret = AVERROR(ENOMEM);
140         goto end;
141     }
142
143 end:
144     if (array)
145         CFRelease(array);
146     return ret;
147 }
148
149 static int load_cert(URLContext *h)
150 {
151     TLSContext *c = h->priv_data;
152     int ret = 0;
153     CFArrayRef certArray = NULL;
154     CFArrayRef keyArray = NULL;
155     SecIdentityRef id = NULL;
156     CFMutableArrayRef outArray = NULL;
157
158     if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
159         goto end;
160
161     if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
162         goto end;
163
164     if (!(id = SecIdentityCreate(kCFAllocatorDefault,
165                                  (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
166                                  (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
167         ret = AVERROR_UNKNOWN;
168         goto end;
169     }
170
171     if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
172         ret = AVERROR(ENOMEM);
173         goto end;
174     }
175
176     CFArraySetValueAtIndex(outArray, 0, id);
177
178     SSLSetCertificate(c->ssl_context, outArray);
179
180 end:
181     if (certArray)
182         CFRelease(certArray);
183     if (keyArray)
184         CFRelease(keyArray);
185     if (outArray)
186         CFRelease(outArray);
187     if (id)
188         CFRelease(id);
189     return ret;
190 }
191
192 static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
193 {
194     URLContext *h = (URLContext*)connection;
195     TLSContext *c = h->priv_data;
196     int read = ffurl_read_complete(c->tls_shared.tcp, data, *dataLength);
197     if (read <= 0) {
198         *dataLength = 0;
199         switch(AVUNERROR(read)) {
200             case ENOENT:
201             case 0:
202                 return errSSLClosedGraceful;
203             case ECONNRESET:
204                 return errSSLClosedAbort;
205             case EAGAIN:
206                 return errSSLWouldBlock;
207             default:
208                 c->lastErr = read;
209                 return ioErr;
210         }
211     } else {
212         *dataLength = read;
213         return noErr;
214     }
215 }
216
217 static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
218 {
219     URLContext *h = (URLContext*)connection;
220     TLSContext *c = h->priv_data;
221     int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
222     if (written <= 0) {
223         *dataLength = 0;
224         switch(AVUNERROR(written)) {
225             case EAGAIN:
226                 return errSSLWouldBlock;
227             default:
228                 c->lastErr = written;
229                 return ioErr;
230         }
231     } else {
232         *dataLength = written;
233         return noErr;
234     }
235 }
236
237 static int tls_close(URLContext *h)
238 {
239     TLSContext *c = h->priv_data;
240     if (c->ssl_context) {
241         SSLClose(c->ssl_context);
242         CFRelease(c->ssl_context);
243     }
244     if (c->ca_array)
245         CFRelease(c->ca_array);
246     if (c->tls_shared.tcp)
247         ffurl_close(c->tls_shared.tcp);
248     return 0;
249 }
250
251 #define CHECK_ERROR(func, ...) do {                                     \
252         OSStatus status = func(__VA_ARGS__);                            \
253         if (status != noErr) {                                          \
254             ret = AVERROR_UNKNOWN;                                      \
255             av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
256             goto fail;                                                  \
257         }                                                               \
258     } while (0)
259
260 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
261 {
262     TLSContext *c = h->priv_data;
263     TLSShared *s = &c->tls_shared;
264     int ret;
265
266     if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
267         goto fail;
268
269     c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
270     if (!c->ssl_context) {
271         av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
272         ret = AVERROR(ENOMEM);
273         goto fail;
274     }
275     if (s->ca_file) {
276         if ((ret = load_ca(h)) < 0)
277             goto fail;
278     }
279     if (s->ca_file || !s->verify)
280         CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
281     if (s->cert_file)
282         if ((ret = load_cert(h)) < 0)
283             goto fail;
284     CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
285     CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
286     CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
287     while (1) {
288         OSStatus status = SSLHandshake(c->ssl_context);
289         if (status == errSSLServerAuthCompleted) {
290             SecTrustRef peerTrust;
291             SecTrustResultType trustResult;
292             if (!s->verify)
293                 continue;
294
295             if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
296                 ret = AVERROR(ENOMEM);
297                 goto fail;
298             }
299
300             if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
301                 ret = AVERROR_UNKNOWN;
302                 goto fail;
303             }
304
305             if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
306                 ret = AVERROR_UNKNOWN;
307                 goto fail;
308             }
309
310             if (trustResult == kSecTrustResultProceed ||
311                 trustResult == kSecTrustResultUnspecified) {
312                 // certificate is trusted
313                 status = errSSLWouldBlock; // so we call SSLHandshake again
314             } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
315                 // not trusted, for some reason other than being expired
316                 status = errSSLXCertChainInvalid;
317             } else {
318                 // cannot use this certificate (fatal)
319                 status = errSSLBadCert;
320             }
321
322             if (peerTrust)
323                 CFRelease(peerTrust);
324         }
325         if (status == noErr)
326             break;
327
328         av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
329         ret = AVERROR(EIO);
330         goto fail;
331     }
332
333     return 0;
334 fail:
335     tls_close(h);
336     return ret;
337 }
338
339 static int map_ssl_error(OSStatus status, size_t processed)
340 {
341     switch (status) {
342     case noErr:
343         return processed;
344     case errSSLClosedGraceful:
345     case errSSLClosedNoNotify:
346         return 0;
347     default:
348         return (int)status;
349     }
350 }
351
352 static int tls_read(URLContext *h, uint8_t *buf, int size)
353 {
354     TLSContext *c = h->priv_data;
355     size_t processed = 0;
356     int ret = SSLRead(c->ssl_context, buf, size, &processed);
357     ret = map_ssl_error(ret, processed);
358     if (ret > 0)
359         return ret;
360     if (ret == 0)
361         return AVERROR_EOF;
362     return print_tls_error(h, ret);
363 }
364
365 static int tls_write(URLContext *h, const uint8_t *buf, int size)
366 {
367     TLSContext *c = h->priv_data;
368     size_t processed = 0;
369     int ret = SSLWrite(c->ssl_context, buf, size, &processed);
370     ret = map_ssl_error(ret, processed);
371     if (ret > 0)
372         return ret;
373     if (ret == 0)
374         return AVERROR_EOF;
375     return print_tls_error(h, ret);
376 }
377
378 static const AVOption options[] = {
379     TLS_COMMON_OPTIONS(TLSContext, tls_shared),
380     { NULL }
381 };
382
383 static const AVClass tls_class = {
384     .class_name = "tls",
385     .item_name  = av_default_item_name,
386     .option     = options,
387     .version    = LIBAVUTIL_VERSION_INT,
388 };
389
390 URLProtocol ff_tls_securetransport_protocol = {
391     .name           = "tls",
392     .url_open2      = tls_open,
393     .url_read       = tls_read,
394     .url_write      = tls_write,
395     .url_close      = tls_close,
396     .priv_data_size = sizeof(TLSContext),
397     .flags          = URL_PROTOCOL_FLAG_NETWORK,
398     .priv_data_class = &tls_class,
399 };