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/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,
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) {
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) {
113  goto end;
114  }
115 
116  if (CFArrayGetCount(*array) == 0) {
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)))) {
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  ffurl_closep(&c->tls_shared.tcp);
255  return 0;
256 }
257 
258 #define CHECK_ERROR(func, ...) do { \
259  OSStatus status = func(__VA_ARGS__); \
260  if (status != noErr) { \
261  ret = AVERROR_UNKNOWN; \
262  av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
263  goto fail; \
264  } \
265  } while (0)
266 
267 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
268 {
269  TLSContext *c = h->priv_data;
270  TLSShared *s = &c->tls_shared;
271  int ret;
272 
273  if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
274  goto fail;
275 
276  c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
277  if (!c->ssl_context) {
278  av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
279  ret = AVERROR(ENOMEM);
280  goto fail;
281  }
282  if (s->ca_file) {
283  if ((ret = load_ca(h)) < 0)
284  goto fail;
285  }
286  if (s->ca_file || !s->verify)
287  CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
288  if (s->cert_file)
289  if ((ret = load_cert(h)) < 0)
290  goto fail;
291  CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
292  CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
293  CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
294  while (1) {
295  OSStatus status = SSLHandshake(c->ssl_context);
296  if (status == errSSLServerAuthCompleted) {
297  SecTrustRef peerTrust;
298  SecTrustResultType trustResult;
299  if (!s->verify)
300  continue;
301 
302  if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
303  ret = AVERROR(ENOMEM);
304  goto fail;
305  }
306 
307  if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
309  goto fail;
310  }
311 
312  if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
314  goto fail;
315  }
316 
317  if (trustResult == kSecTrustResultProceed ||
318  trustResult == kSecTrustResultUnspecified) {
319  // certificate is trusted
320  status = errSSLWouldBlock; // so we call SSLHandshake again
321  } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
322  // not trusted, for some reason other than being expired
323  status = errSSLXCertChainInvalid;
324  } else {
325  // cannot use this certificate (fatal)
326  status = errSSLBadCert;
327  }
328 
329  if (peerTrust)
330  CFRelease(peerTrust);
331  }
332  if (status == noErr) {
333  break;
334  } else if (status != errSSLWouldBlock) {
335  av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
336  ret = AVERROR(EIO);
337  goto fail;
338  }
339  }
340 
341  return 0;
342 fail:
343  tls_close(h);
344  return ret;
345 }
346 
347 static int map_ssl_error(OSStatus status, size_t processed)
348 {
349  switch (status) {
350  case noErr:
351  return processed;
352  case errSSLClosedGraceful:
353  case errSSLClosedNoNotify:
354  return 0;
355  case errSSLWouldBlock:
356  if (processed > 0)
357  return processed;
358  default:
359  return (int)status;
360  }
361 }
362 
363 static int tls_read(URLContext *h, uint8_t *buf, int size)
364 {
365  TLSContext *c = h->priv_data;
366  size_t available = 0, processed = 0;
367  int ret;
368  SSLGetBufferedReadSize(c->ssl_context, &available);
369  if (available)
370  size = FFMIN(available, size);
371  ret = SSLRead(c->ssl_context, buf, size, &processed);
373  if (ret > 0)
374  return ret;
375  if (ret == 0)
376  return AVERROR_EOF;
377  return print_tls_error(h, ret);
378 }
379 
380 static int tls_write(URLContext *h, const uint8_t *buf, int size)
381 {
382  TLSContext *c = h->priv_data;
383  size_t processed = 0;
384  int ret = SSLWrite(c->ssl_context, buf, size, &processed);
386  if (ret > 0)
387  return ret;
388  if (ret == 0)
389  return AVERROR_EOF;
390  return print_tls_error(h, ret);
391 }
392 
394 {
395  TLSContext *c = h->priv_data;
396  return ffurl_get_file_handle(c->tls_shared.tcp);
397 }
398 
400 {
401  TLSContext *s = h->priv_data;
402  return ffurl_get_short_seek(s->tls_shared.tcp);
403 }
404 
405 static const AVOption options[] = {
406  TLS_COMMON_OPTIONS(TLSContext, tls_shared),
407  { NULL }
408 };
409 
410 static const AVClass tls_class = {
411  .class_name = "tls",
412  .item_name = av_default_item_name,
413  .option = options,
414  .version = LIBAVUTIL_VERSION_INT,
415 };
416 
418  .name = "tls",
419  .url_open2 = tls_open,
420  .url_read = tls_read,
421  .url_write = tls_write,
422  .url_close = tls_close,
423  .url_get_file_handle = tls_get_file_handle,
424  .url_get_short_seek = tls_get_short_seek,
425  .priv_data_size = sizeof(TLSContext),
427  .priv_data_class = &tls_class,
428 };
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:258
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:33
avio_close
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1271
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:52
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:208
tls_open
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
Definition: tls_securetransport.c:267
internal.h
map_ssl_error
static int map_ssl_error(OSStatus status, size_t processed)
Definition: tls_securetransport.c:347
AVOption
AVOption.
Definition: opt.h:251
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:380
ffio_open_whitelist
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:1244
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:370
URLProtocol
Definition: url.h:53
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:138
ffurl_get_short_seek
int ffurl_get_short_seek(void *urlcontext)
Return the current short seek threshold value for this URL.
Definition: avio.c:651
tls_write_cb
static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
Definition: tls_securetransport.c:225
tls_read_cb
static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
Definition: tls_securetransport.c:196
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:399
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:133
s
#define s(width, name)
Definition: cbs_vp9.c:198
ioErr
#define ioErr
Definition: tls_securetransport.c:42
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:49
TLSContext::ca_array
CFArrayRef ca_array
Definition: tls_securetransport.c:48
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:166
tls_get_file_handle
static int tls_get_file_handle(URLContext *h)
Definition: tls_securetransport.c:393
tls_read
static int tls_read(URLContext *h, uint8_t *buf, int size)
Definition: tls_securetransport.c:363
tls_class
static const AVClass tls_class
Definition: tls_securetransport.c:410
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:54
import_pem
static int import_pem(URLContext *h, char *path, CFArrayRef *array)
Definition: tls_securetransport.c:70
TLSContext::ssl_context
SSLContextRef ssl_context
Definition: tls_securetransport.c:47
tls_close
static int tls_close(URLContext *h)
Definition: tls_securetransport.c:245
URLContext
Definition: url.h:37
avio_internal.h
available
if no frame is available
Definition: filter_design.txt:166
options
static const AVOption options[]
Definition: tls_securetransport.c:405
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:439
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:68
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:119
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:659
load_cert
static int load_cert(URLContext *h)
Definition: tls_securetransport.c:153
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:636
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
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:417
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:627
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:183