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,
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  if (c->tls_shared.tcp)
255  ffurl_close(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 
400 static const AVOption options[] = {
401  TLS_COMMON_OPTIONS(TLSContext, tls_shared),
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 };
TLSContext
Definition: tls_gnutls.c:48
status
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
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:34
avio_close
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1185
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:55
print_tls_error
static int print_tls_error(URLContext *h, int ret)
Definition: tls_securetransport.c:52
tls_open
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
Definition: tls_securetransport.c:268
end
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:90
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:246
data
const char data[16]
Definition: mxf.c:91
tls_write
static int tls_write(URLContext *h, const uint8_t *buf, int size)
Definition: tls_securetransport.c:381
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:1158
ffurl_close
int ffurl_close(URLContext *h)
Definition: avio.c:470
AVDictionary
Definition: dict.c:30
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:71
avio_size
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:336
URLProtocol
Definition: url.h:54
os_support.h
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:31
AVUNERROR
#define AVUNERROR(e)
Definition: error.h:44
TLS_COMMON_OPTIONS
#define TLS_COMMON_OPTIONS(pstruct, options_field)
Definition: tls.h:45
fail
#define fail()
Definition: checkasm.h:120
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
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
buf
void * buf
Definition: avisynth_c.h:766
load_ca
static int load_ca(URLContext *h)
Definition: tls_securetransport.c:133
s
#define s(width, name)
Definition: cbs_vp9.c:257
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:67
NULL
#define NULL
Definition: coverity.c:32
AVERROR_PATCHWELCOME
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:62
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:191
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:161
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:405
size
int size
Definition: twinvq_data.h:11134
TLSContext::tls_shared
TLSShared tls_shared
Definition: tls_gnutls.c:50
URLProtocol::name
const char * name
Definition: url.h:55
FFMIN
#define FFMIN(a, b)
Definition: common.h:96
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:38
avio_internal.h
available
if no frame is available
Definition: filter_design.txt:166
options
static const AVOption options[]
Definition: tls_securetransport.c:400
url.h
uint8_t
uint8_t
Definition: audio_convert.c:194
array
static int array[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:106
ff_tls_open_underlying
int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options)
Definition: tls.c:56
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:72
avformat.h
network.h
tls.h
ffurl_read
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:410
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:647
ffurl_write
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:424
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:654
TLSShared
Definition: tls.h:29
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:565
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
ff_tls_protocol
const URLProtocol ff_tls_protocol
Definition: tls_securetransport.c:412
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:629