FFmpeg
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 "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;
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,
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)
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 
395 {
396  TLSContext *c = h->priv_data;
398 }
399 
400 static const AVOption options[] = {
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 
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),
421  .priv_data_class = &tls_class,
422 };
#define NULL
Definition: coverity.c:32
static const AVClass tls_class
Bytestream IO Context.
Definition: avio.h:161
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
static int tls_get_file_handle(URLContext *h)
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:339
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
#define CHECK_ERROR(func,...)
AVOption.
Definition: opt.h:246
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:100
int verify
Definition: tls.h:31
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
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:420
status_in is a status change that must be taken into account after all frames in fifo have been processed
AVIOInterruptCB interrupt_callback
Definition: url.h:47
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
GLint GLenum type
Definition: opengl_enc.c:104
int listen
Definition: tls.h:34
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample format(the sample packing is implied by the sample format) and sample rate.The lists are not just lists
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)
const URLProtocol ff_tls_protocol
AVOptions.
miscellaneous OS support macros and functions.
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:90
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
CFArrayRef ca_array
Definition: tls.h:29
#define AVERROR_EOF
End of file.
Definition: error.h:55
ptrdiff_t size
Definition: opengl_enc.c:100
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:650
#define ioErr
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
if no frame is available
const char * protocol_whitelist
Definition: url.h:49
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1217
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:122
char * host
Definition: tls.h:36
mbedtls_ssl_context ssl_context
Definition: tls_mbedtls.c:40
#define FFMIN(a, b)
Definition: common.h:96
#define TLS_COMMON_OPTIONS(pstruct, options_field)
Definition: tls.h:45
#define s(width, name)
Definition: cbs_vp9.c:257
char * cert_file
Definition: tls.h:32
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
Definition: avio.c:625
static int map_ssl_error(OSStatus status, size_t processed)
char * ca_file
Definition: tls.h:30
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:62
const char * protocol_blacklist
Definition: url.h:50
TLSShared tls_shared
Definition: tls_gnutls.c:50
void * buf
Definition: avisynth_c.h:766
Definition: url.h:38
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
they must not be accessed directly The fifo field contains the frames that are queued in the input for processing by the filter The status_in and status_out fields contains the queued status(EOF or error) of the link
SSLContextRef ssl_context
int ffio_open_whitelist(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist)
Definition: aviobuf.c:1192
misc parsing utilities
const char * name
Definition: url.h:55
#define flags(name, subs,...)
Definition: cbs_av1.c:561
static const AVOption options[]
int ffurl_close(URLContext *h)
Definition: avio.c:466
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 int tls_write(URLContext *h, const uint8_t *buf, int size)
URLContext * tcp
Definition: tls.h:41
#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 array[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:106
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
static int load_cert(URLContext *h)
int ffurl_read(URLContext *h, unsigned char *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf...
Definition: avio.c:406
char * key_file
Definition: tls.h:33