FFmpeg
tls_securetransport.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 rcombs
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/mem.h"
34 #include "libavutil/opt.h"
35 #include "libavutil/parseutils.h"
36 
37 #include <Security/Security.h>
38 #include <Security/SecureTransport.h>
39 #include <CoreFoundation/CoreFoundation.h>
40 
41 // We use a private API call here; it's good enough for WebKit.
42 SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
43 #define ioErr -36
44 
45 typedef struct TLSContext {
46  const AVClass *class;
48  SSLContextRef ssl_context;
49  CFArrayRef ca_array;
50  int lastErr;
51 } TLSContext;
52 
53 static int print_tls_error(URLContext *h, int ret)
54 {
55  TLSContext *c = h->priv_data;
56  switch (ret) {
57  case errSSLWouldBlock:
58  return AVERROR(EAGAIN);
59  case errSSLXCertChainInvalid:
60  av_log(h, AV_LOG_ERROR, "Invalid certificate chain\n");
61  return AVERROR(EIO);
62  case ioErr:
63  return c->lastErr;
64  default:
65  av_log(h, AV_LOG_ERROR, "IO Error: %i\n", ret);
66  return AVERROR(EIO);
67  }
68  return AVERROR(EIO);
69 }
70 
71 static int import_pem(URLContext *h, char *path, CFArrayRef *array)
72 {
73 #if !HAVE_SECITEMIMPORT
74  return AVERROR_PATCHWELCOME;
75 #else
76  AVIOContext *s = NULL;
77  CFDataRef data = NULL;
78  int64_t ret = 0;
79  char *buf = NULL;
80  SecExternalFormat format = kSecFormatPEMSequence;
81  SecExternalFormat type = kSecItemTypeAggregate;
82  CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
83  if (!pathStr) {
84  ret = AVERROR(ENOMEM);
85  goto end;
86  }
87 
88  if ((ret = ffio_open_whitelist(&s, path, AVIO_FLAG_READ,
89  &h->interrupt_callback, NULL,
90  h->protocol_whitelist, h->protocol_blacklist)) < 0)
91  goto end;
92 
93  if ((ret = avio_size(s)) < 0)
94  goto end;
95 
96  if (ret == 0) {
98  goto end;
99  }
100 
101  if (!(buf = av_malloc(ret))) {
102  ret = AVERROR(ENOMEM);
103  goto end;
104  }
105 
106  if ((ret = avio_read(s, buf, ret)) < 0)
107  goto end;
108 
109  data = CFDataCreate(kCFAllocatorDefault, buf, ret);
110 
111  if (SecItemImport(data, pathStr, &format, &type,
112  0, NULL, NULL, array) != noErr || !array) {
114  goto end;
115  }
116 
117  if (CFArrayGetCount(*array) == 0) {
119  goto end;
120  }
121 
122 end:
123  av_free(buf);
124  if (pathStr)
125  CFRelease(pathStr);
126  if (data)
127  CFRelease(data);
128  if (s)
129  avio_close(s);
130  return ret;
131 #endif
132 }
133 
134 static int load_ca(URLContext *h)
135 {
136  TLSContext *c = h->priv_data;
137  int ret = 0;
138  CFArrayRef array = NULL;
139 
140  if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
141  goto end;
142 
143  if (!(c->ca_array = CFRetain(array))) {
144  ret = AVERROR(ENOMEM);
145  goto end;
146  }
147 
148 end:
149  if (array)
150  CFRelease(array);
151  return ret;
152 }
153 
154 static int load_cert(URLContext *h)
155 {
156  TLSContext *c = h->priv_data;
157  int ret = 0;
158  CFArrayRef certArray = NULL;
159  CFArrayRef keyArray = NULL;
160  SecIdentityRef id = NULL;
161  CFMutableArrayRef outArray = NULL;
162 
163  if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
164  goto end;
165 
166  if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
167  goto end;
168 
169  if (!(id = SecIdentityCreate(kCFAllocatorDefault,
170  (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
171  (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
173  goto end;
174  }
175 
176  if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
177  ret = AVERROR(ENOMEM);
178  goto end;
179  }
180 
181  CFArraySetValueAtIndex(outArray, 0, id);
182 
183  SSLSetCertificate(c->ssl_context, outArray);
184 
185 end:
186  if (certArray)
187  CFRelease(certArray);
188  if (keyArray)
189  CFRelease(keyArray);
190  if (outArray)
191  CFRelease(outArray);
192  if (id)
193  CFRelease(id);
194  return ret;
195 }
196 
197 static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
198 {
199  URLContext *h = (URLContext*)connection;
200  TLSContext *c = h->priv_data;
201  size_t requested = *dataLength;
202  int read = ffurl_read(c->tls_shared.tcp, data, requested);
203  if (read <= 0) {
204  *dataLength = 0;
205  switch(AVUNERROR(read)) {
206  case ENOENT:
207  case 0:
208  return errSSLClosedGraceful;
209  case ECONNRESET:
210  return errSSLClosedAbort;
211  case EAGAIN:
212  return errSSLWouldBlock;
213  default:
214  c->lastErr = read;
215  return ioErr;
216  }
217  } else {
218  *dataLength = read;
219  if (read < requested)
220  return errSSLWouldBlock;
221  else
222  return noErr;
223  }
224 }
225 
226 static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
227 {
228  URLContext *h = (URLContext*)connection;
229  TLSContext *c = h->priv_data;
230  int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
231  if (written <= 0) {
232  *dataLength = 0;
233  switch(AVUNERROR(written)) {
234  case EAGAIN:
235  return errSSLWouldBlock;
236  default:
237  c->lastErr = written;
238  return ioErr;
239  }
240  } else {
241  *dataLength = written;
242  return noErr;
243  }
244 }
245 
246 static int tls_close(URLContext *h)
247 {
248  TLSContext *c = h->priv_data;
249  if (c->ssl_context) {
250  SSLClose(c->ssl_context);
251  CFRelease(c->ssl_context);
252  }
253  if (c->ca_array)
254  CFRelease(c->ca_array);
255  ffurl_closep(&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) {
310  goto fail;
311  }
312 
313  if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
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);
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);
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;
397  return ffurl_get_file_handle(c->tls_shared.tcp);
398 }
399 
401 {
402  TLSContext *s = h->priv_data;
403  return ffurl_get_short_seek(s->tls_shared.tcp);
404 }
405 
406 static const AVOption options[] = {
407  TLS_COMMON_OPTIONS(TLSContext, tls_shared),
408  { NULL }
409 };
410 
411 static const AVClass tls_class = {
412  .class_name = "tls",
413  .item_name = av_default_item_name,
414  .option = options,
415  .version = LIBAVUTIL_VERSION_INT,
416 };
417 
419  .name = "tls",
420  .url_open2 = tls_open,
421  .url_read = tls_read,
422  .url_write = tls_write,
423  .url_close = tls_close,
424  .url_get_file_handle = tls_get_file_handle,
425  .url_get_short_seek = tls_get_short_seek,
426  .priv_data_size = sizeof(TLSContext),
428  .priv_data_class = &tls_class,
429 };
ffio_open_whitelist
int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist)
Definition: avio.c:471
TLSContext
Definition: tls_gnutls.c:48
AVERROR
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
opt.h
CHECK_ERROR
#define CHECK_ERROR(func,...)
Definition: tls_securetransport.c:259
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:33
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
print_tls_error
static int print_tls_error(URLContext *h, int ret)
Definition: tls_securetransport.c:53
ffurl_write
static int ffurl_write(URLContext *h, const uint8_t *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: url.h:202
tls_open
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
Definition: tls_securetransport.c:268
internal.h
map_ssl_error
static int map_ssl_error(OSStatus status, size_t processed)
Definition: tls_securetransport.c:348
AVOption
AVOption.
Definition: opt.h:346
data
const char data[16]
Definition: mxf.c:148
tls_write
static int tls_write(URLContext *h, const uint8_t *buf, int size)
Definition: tls_securetransport.c:381
AVDictionary
Definition: dict.c:34
processed
status_in is a status change that must be taken into account after all frames in fifo have been processed
Definition: filter_design.txt:159
AVERROR_UNKNOWN
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition: error.h:73
avio_size
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:323
URLProtocol
Definition: url.h:51
os_support.h
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
AVUNERROR
#define AVUNERROR(e)
Definition: error.h:46
TLS_COMMON_OPTIONS
#define TLS_COMMON_OPTIONS(pstruct, options_field)
Definition: tls.h:46
fail
#define fail()
Definition: checkasm.h:179
ffurl_get_short_seek
int ffurl_get_short_seek(void *urlcontext)
Return the current short seek threshold value for this URL.
Definition: avio.c:838
tls_write_cb
static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
Definition: tls_securetransport.c:226
tls_read_cb
static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
Definition: tls_securetransport.c:197
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
tls_get_short_seek
static int tls_get_short_seek(URLContext *h)
Definition: tls_securetransport.c:400
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
load_ca
static int load_ca(URLContext *h)
Definition: tls_securetransport.c:134
s
#define s(width, name)
Definition: cbs_vp9.c:198
ioErr
#define ioErr
Definition: tls_securetransport.c:43
format
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
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:66
NULL
#define NULL
Definition: coverity.c:32
AVERROR_PATCHWELCOME
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:64
TLSContext::lastErr
int lastErr
Definition: tls_securetransport.c:50
TLSContext::ca_array
CFArrayRef ca_array
Definition: tls_securetransport.c:49
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:237
parseutils.h
c
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
SecIdentityCreate
SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey)
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
tls_get_file_handle
static int tls_get_file_handle(URLContext *h)
Definition: tls_securetransport.c:394
tls_read
static int tls_read(URLContext *h, uint8_t *buf, int size)
Definition: tls_securetransport.c:364
tls_class
static const AVClass tls_class
Definition: tls_securetransport.c:411
size
int size
Definition: twinvq_data.h:10344
TLSContext::tls_shared
TLSShared tls_shared
Definition: tls_gnutls.c:50
URLProtocol::name
const char * name
Definition: url.h:52
import_pem
static int import_pem(URLContext *h, char *path, CFArrayRef *array)
Definition: tls_securetransport.c:71
TLSContext::ssl_context
SSLContextRef ssl_context
Definition: tls_securetransport.c:48
tls_close
static int tls_close(URLContext *h)
Definition: tls_securetransport.c:246
URLContext
Definition: url.h:35
avio_internal.h
available
if no frame is available
Definition: filter_design.txt:166
options
static const AVOption options[]
Definition: tls_securetransport.c:406
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
url.h
ffurl_closep
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
Definition: avio.c:588
array
static int array[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:111
ff_tls_open_underlying
int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options)
Definition: tls.c:67
ret
ret
Definition: filter_design.txt:187
AVClass::class_name
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:71
avformat.h
network.h
tls.h
status
ov_status_e status
Definition: dnn_backend_openvino.c:121
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:612
load_cert
static int load_cert(URLContext *h)
Definition: tls_securetransport.c:154
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:617
mem.h
TLSShared
Definition: tls.h:29
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:474
avio_close
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: avio.c:616
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
ff_tls_protocol
const URLProtocol ff_tls_protocol
Definition: tls_securetransport.c:418
h
h
Definition: vp9dsp_template.c:2038
avstring.h
ffurl_get_file_handle
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
Definition: avio.c:814
read
static uint32_t BS_FUNC() read(BSCTX *bc, unsigned int n)
Return n bits from the buffer, n has to be in the 0-32 range.
Definition: bitstream_template.h:231
ffurl_read
static int ffurl_read(URLContext *h, uint8_t *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf.
Definition: url.h:181