FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tls_securetransport.c
Go to the documentation of this file.
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 "internal.h"
26 #include "network.h"
27 #include "os_support.h"
28 #include "url.h"
29 #include "tls.h"
30 #include "libavcodec/internal.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/parseutils.h"
34 
35 #include <Security/Security.h>
36 #include <Security/SecureTransport.h>
37 #include <CoreFoundation/CoreFoundation.h>
38 
39 // We use a private API call here; it's good enough for WebKit.
40 SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
41 #define ioErr -36
42 
43 typedef struct TLSContext {
44  const AVClass *class;
46  SSLContextRef ssl_context;
47  CFArrayRef ca_array;
48  int lastErr;
49 } TLSContext;
50 
51 static int print_tls_error(URLContext *h, int ret)
52 {
53  TLSContext *c = h->priv_data;
54  switch (ret) {
55  case errSSLWouldBlock:
56  break;
57  case errSSLXCertChainInvalid:
58  av_log(h, AV_LOG_ERROR, "Invalid certificate chain\n");
59  return AVERROR(EIO);
60  case ioErr:
61  return c->lastErr;
62  default:
63  av_log(h, AV_LOG_ERROR, "IO Error: %i\n", ret);
64  return AVERROR(EIO);
65  }
66  return AVERROR(EIO);
67 }
68 
69 static int import_pem(URLContext *h, char *path, CFArrayRef *array)
70 {
71  AVIOContext *s = NULL;
72  CFDataRef data = NULL;
73  int64_t ret = 0;
74  char *buf = NULL;
75  SecExternalFormat format = kSecFormatPEMSequence;
76  SecExternalFormat type = kSecItemTypeAggregate;
77  CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
78  if (!pathStr) {
79  ret = AVERROR(ENOMEM);
80  goto end;
81  }
82 
83  if ((ret = avio_open2(&s, path, AVIO_FLAG_READ,
84  &h->interrupt_callback, NULL)) < 0)
85  goto end;
86 
87  if ((ret = avio_size(s)) < 0)
88  goto end;
89 
90  if (ret == 0) {
91  ret = AVERROR_INVALIDDATA;
92  goto end;
93  }
94 
95  if (!(buf = av_malloc(ret))) {
96  ret = AVERROR(ENOMEM);
97  goto end;
98  }
99 
100  if ((ret = avio_read(s, buf, ret)) < 0)
101  goto end;
102 
103  data = CFDataCreate(kCFAllocatorDefault, buf, ret);
104 
105  if (SecItemImport(data, pathStr, &format, &type,
106  0, NULL, NULL, array) != noErr || !array) {
107  ret = AVERROR_UNKNOWN;
108  goto end;
109  }
110 
111  if (CFArrayGetCount(*array) == 0) {
112  ret = AVERROR_INVALIDDATA;
113  goto end;
114  }
115 
116 end:
117  av_free(buf);
118  if (pathStr)
119  CFRelease(pathStr);
120  if (data)
121  CFRelease(data);
122  if (s)
123  avio_close(s);
124  return ret;
125 }
126 
127 static int load_ca(URLContext *h)
128 {
129  TLSContext *c = h->priv_data;
130  int ret = 0;
131  CFArrayRef array = NULL;
132 
133  if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
134  goto end;
135 
136  if (!(c->ca_array = CFRetain(array))) {
137  ret = AVERROR(ENOMEM);
138  goto end;
139  }
140 
141 end:
142  if (array)
143  CFRelease(array);
144  return ret;
145 }
146 
147 static int load_cert(URLContext *h)
148 {
149  TLSContext *c = h->priv_data;
150  int ret = 0;
151  CFArrayRef certArray = NULL;
152  CFArrayRef keyArray = NULL;
153  SecIdentityRef id = NULL;
154  CFMutableArrayRef outArray = NULL;
155 
156  if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
157  goto end;
158 
159  if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
160  goto end;
161 
162  if (!(id = SecIdentityCreate(kCFAllocatorDefault,
163  (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
164  (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
165  ret = AVERROR_UNKNOWN;
166  goto end;
167  }
168 
169  if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
170  ret = AVERROR(ENOMEM);
171  goto end;
172  }
173 
174  CFArraySetValueAtIndex(outArray, 0, id);
175 
176  SSLSetCertificate(c->ssl_context, outArray);
177 
178 end:
179  if (certArray)
180  CFRelease(certArray);
181  if (keyArray)
182  CFRelease(keyArray);
183  if (outArray)
184  CFRelease(outArray);
185  if (id)
186  CFRelease(id);
187  return ret;
188 }
189 
190 static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
191 {
192  URLContext *h = (URLContext*)connection;
193  TLSContext *c = h->priv_data;
194  int read = ffurl_read_complete(c->tls_shared.tcp, data, *dataLength);
195  if (read <= 0) {
196  *dataLength = 0;
197  switch(AVUNERROR(read)) {
198  case ENOENT:
199  case 0:
200  return errSSLClosedGraceful;
201  case ECONNRESET:
202  return errSSLClosedAbort;
203  case EAGAIN:
204  return errSSLWouldBlock;
205  default:
206  c->lastErr = read;
207  return ioErr;
208  }
209  } else {
210  *dataLength = read;
211  return noErr;
212  }
213 }
214 
215 static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
216 {
217  URLContext *h = (URLContext*)connection;
218  TLSContext *c = h->priv_data;
219  int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
220  if (written <= 0) {
221  *dataLength = 0;
222  switch(AVUNERROR(written)) {
223  case EAGAIN:
224  return errSSLWouldBlock;
225  default:
226  c->lastErr = written;
227  return ioErr;
228  }
229  } else {
230  *dataLength = written;
231  return noErr;
232  }
233 }
234 
235 static int tls_close(URLContext *h)
236 {
237  TLSContext *c = h->priv_data;
238  if (c->ssl_context) {
239  SSLClose(c->ssl_context);
240  CFRelease(c->ssl_context);
241  }
242  if (c->ca_array)
243  CFRelease(c->ca_array);
244  if (c->tls_shared.tcp)
246  return 0;
247 }
248 
249 #define CHECK_ERROR(func, ...) do { \
250  OSStatus status = func(__VA_ARGS__); \
251  if (status != noErr) { \
252  ret = AVERROR_UNKNOWN; \
253  av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
254  goto fail; \
255  } \
256  } while (0)
257 
258 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
259 {
260  TLSContext *c = h->priv_data;
261  TLSShared *s = &c->tls_shared;
262  int ret;
263 
264  if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
265  goto fail;
266 
267  c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
268  if (!c->ssl_context) {
269  av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
270  ret = AVERROR(ENOMEM);
271  goto fail;
272  }
273  if (s->ca_file) {
274  if ((ret = load_ca(h)) < 0)
275  goto fail;
276  }
277  if (s->ca_file || !s->verify)
278  CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
279  if (s->cert_file)
280  if ((ret = load_cert(h)) < 0)
281  goto fail;
282  CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
283  CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
284  CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
285  while (1) {
286  OSStatus status = SSLHandshake(c->ssl_context);
287  if (status == errSSLServerAuthCompleted) {
288  SecTrustRef peerTrust;
289  SecTrustResultType trustResult;
290  if (!s->verify)
291  continue;
292 
293  if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
294  ret = AVERROR(ENOMEM);
295  goto fail;
296  }
297 
298  if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
299  ret = AVERROR_UNKNOWN;
300  goto fail;
301  }
302 
303  if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
304  ret = AVERROR_UNKNOWN;
305  goto fail;
306  }
307 
308  if (trustResult == kSecTrustResultProceed ||
309  trustResult == kSecTrustResultUnspecified) {
310  // certificate is trusted
311  status = errSSLWouldBlock; // so we call SSLHandshake again
312  } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
313  // not trusted, for some reason other than being expired
314  status = errSSLXCertChainInvalid;
315  } else {
316  // cannot use this certificate (fatal)
317  status = errSSLBadCert;
318  }
319 
320  if (peerTrust)
321  CFRelease(peerTrust);
322  }
323  if (status == noErr)
324  break;
325 
326  av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
327  ret = AVERROR(EIO);
328  goto fail;
329  }
330 
331  return 0;
332 fail:
333  tls_close(h);
334  return ret;
335 }
336 
337 static int map_ssl_error(OSStatus status, size_t processed)
338 {
339  switch (status) {
340  case noErr:
341  return processed;
342  case errSSLClosedGraceful:
343  case errSSLClosedNoNotify:
344  return 0;
345  default:
346  return (int)status;
347  }
348 }
349 
350 static int tls_read(URLContext *h, uint8_t *buf, int size)
351 {
352  TLSContext *c = h->priv_data;
353  size_t processed;
354  int ret = map_ssl_error(SSLRead(c->ssl_context, buf, size, &processed), processed);
355  if (ret > 0)
356  return ret;
357  if (ret == 0)
358  return AVERROR_EOF;
359  return print_tls_error(h, ret);
360 }
361 
362 static int tls_write(URLContext *h, const uint8_t *buf, int size)
363 {
364  TLSContext *c = h->priv_data;
365  size_t processed;
366  int ret = map_ssl_error(SSLWrite(c->ssl_context, buf, size, &processed), processed);
367  if (ret > 0)
368  return ret;
369  if (ret == 0)
370  return AVERROR_EOF;
371  return print_tls_error(h, ret);
372 }
373 
374 static const AVOption options[] = {
375  TLS_COMMON_OPTIONS(TLSContext, tls_shared),
376  { NULL }
377 };
378 
379 static const AVClass tls_class = {
380  .class_name = "tls",
381  .item_name = av_default_item_name,
382  .option = options,
383  .version = LIBAVUTIL_VERSION_INT,
384 };
385 
387  .name = "tls",
388  .url_open2 = tls_open,
389  .url_read = tls_read,
390  .url_write = tls_write,
391  .url_close = tls_close,
392  .priv_data_size = sizeof(TLSContext),
394  .priv_data_class = &tls_class,
395 };
#define NULL
Definition: coverity.c:32
static const AVClass tls_class
const char * s
Definition: avisynth_c.h:631
Bytestream IO Context.
Definition: avio.h:111
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:282
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:35
#define CHECK_ERROR(func,...)
AVOption.
Definition: opt.h:255
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:101
int verify
Definition: tls.h:33
#define LIBAVUTIL_VERSION_INT
Definition: version.h:62
int ffurl_write(URLContext *h, const unsigned char *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: avio.c:367
AVIOInterruptCB interrupt_callback
Definition: url.h:48
#define AVIO_FLAG_READ
read-only
Definition: avio.h:485
int listen
Definition: tls.h:36
static int tls_close(URLContext *h)
static int print_tls_error(URLContext *h, int ret)
static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
uint8_t
#define av_malloc(s)
AVOptions.
miscellaneous OS support macros and functions.
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:90
CFArrayRef ca_array
Definition: tls.h:31
#define AVERROR_EOF
End of file.
Definition: error.h:55
ptrdiff_t size
Definition: opengl_enc.c:101
static int load_ca(URLContext *h)
#define av_log(a,...)
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:538
#define ioErr
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
av_default_item_name
#define AVERROR(e)
Definition: error.h:43
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:941
static int tls_read(URLContext *h, uint8_t *buf, int size)
static int import_pem(URLContext *h, char *path, CFArrayRef *array)
#define fail()
Definition: checkasm.h:57
char * host
Definition: tls.h:38
#define TLS_COMMON_OPTIONS(pstruct, options_field)
Definition: tls.h:47
char * cert_file
Definition: tls.h:34
static int map_ssl_error(OSStatus status, size_t processed)
URLProtocol ff_tls_securetransport_protocol
char * ca_file
Definition: tls.h:32
TLSShared tls_shared
Definition: tls_gnutls.c:46
void * buf
Definition: avisynth_c.h:553
Definition: url.h:39
GLint GLenum type
Definition: opengl_enc.c:105
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:42
int avio_open2(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options)
Create and initialize a AVIOContext for accessing the resource indicated by url.
Definition: aviobuf.c:918
SSLContextRef ssl_context
misc parsing utilities
const char * name
Definition: url.h:53
static const AVOption options[]
static int flags
Definition: cpu.c:47
int ffurl_close(URLContext *h)
Definition: avio.c:412
Main libavformat public API header.
common internal api header.
int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options)
Definition: tls.c:56
static double c[64]
int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
Read as many bytes as possible (up to size), calling the read function multiple times if necessary...
Definition: avio.c:360
static int tls_write(URLContext *h, const uint8_t *buf, int size)
URLContext * tcp
Definition: tls.h:43
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition: error.h:71
static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
#define AVUNERROR(e)
Definition: error.h:44
#define av_free(p)
SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey)
unbuffered private I/O API
static int load_cert(URLContext *h)
char * key_file
Definition: tls.h:35