FFmpeg  4.0
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
const char * s
Definition: avisynth_c.h:768
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:336
static const char * format[]
Definition: af_aiir.c:311
int size
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
#define CHECK_ERROR(func,...)
AVOption.
Definition: opt.h:246
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:421
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:654
int listen
Definition: tls.h:34
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
const char data[16]
Definition: mxf.c:90
CFArrayRef ca_array
Definition: tls.h:29
static int flags
Definition: log.c:55
#define AVERROR_EOF
End of file.
Definition: error.h:55
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:648
#define ioErr
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
const char * protocol_whitelist
Definition: url.h:49
#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:1190
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:116
char * host
Definition: tls.h:36
#define FFMIN(a, b)
Definition: common.h:96
#define TLS_COMMON_OPTIONS(pstruct, options_field)
Definition: tls.h:45
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:626
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:690
Definition: url.h:38
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
cl_device_type type
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:1159
misc parsing utilities
const char * name
Definition: url.h:55
static const AVOption options[]
int ffurl_close(URLContext *h)
Definition: avio.c:467
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]
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
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:407
char * key_file
Definition: tls.h:33