FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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  break;
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  AVIOContext *s = NULL;
73  CFDataRef data = NULL;
74  int64_t ret = 0;
75  char *buf = NULL;
76  SecExternalFormat format = kSecFormatPEMSequence;
77  SecExternalFormat type = kSecItemTypeAggregate;
78  CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
79  if (!pathStr) {
80  ret = AVERROR(ENOMEM);
81  goto end;
82  }
83 
84  if ((ret = ffio_open_whitelist(&s, path, AVIO_FLAG_READ,
87  goto end;
88 
89  if ((ret = avio_size(s)) < 0)
90  goto end;
91 
92  if (ret == 0) {
93  ret = AVERROR_INVALIDDATA;
94  goto end;
95  }
96 
97  if (!(buf = av_malloc(ret))) {
98  ret = AVERROR(ENOMEM);
99  goto end;
100  }
101 
102  if ((ret = avio_read(s, buf, ret)) < 0)
103  goto end;
104 
105  data = CFDataCreate(kCFAllocatorDefault, buf, ret);
106 
107  if (SecItemImport(data, pathStr, &format, &type,
108  0, NULL, NULL, array) != noErr || !array) {
109  ret = AVERROR_UNKNOWN;
110  goto end;
111  }
112 
113  if (CFArrayGetCount(*array) == 0) {
114  ret = AVERROR_INVALIDDATA;
115  goto end;
116  }
117 
118 end:
119  av_free(buf);
120  if (pathStr)
121  CFRelease(pathStr);
122  if (data)
123  CFRelease(data);
124  if (s)
125  avio_close(s);
126  return ret;
127 }
128 
129 static int load_ca(URLContext *h)
130 {
131  TLSContext *c = h->priv_data;
132  int ret = 0;
133  CFArrayRef array = NULL;
134 
135  if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
136  goto end;
137 
138  if (!(c->ca_array = CFRetain(array))) {
139  ret = AVERROR(ENOMEM);
140  goto end;
141  }
142 
143 end:
144  if (array)
145  CFRelease(array);
146  return ret;
147 }
148 
149 static int load_cert(URLContext *h)
150 {
151  TLSContext *c = h->priv_data;
152  int ret = 0;
153  CFArrayRef certArray = NULL;
154  CFArrayRef keyArray = NULL;
155  SecIdentityRef id = NULL;
156  CFMutableArrayRef outArray = NULL;
157 
158  if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
159  goto end;
160 
161  if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
162  goto end;
163 
164  if (!(id = SecIdentityCreate(kCFAllocatorDefault,
165  (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
166  (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
167  ret = AVERROR_UNKNOWN;
168  goto end;
169  }
170 
171  if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
172  ret = AVERROR(ENOMEM);
173  goto end;
174  }
175 
176  CFArraySetValueAtIndex(outArray, 0, id);
177 
178  SSLSetCertificate(c->ssl_context, outArray);
179 
180 end:
181  if (certArray)
182  CFRelease(certArray);
183  if (keyArray)
184  CFRelease(keyArray);
185  if (outArray)
186  CFRelease(outArray);
187  if (id)
188  CFRelease(id);
189  return ret;
190 }
191 
192 static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
193 {
194  URLContext *h = (URLContext*)connection;
195  TLSContext *c = h->priv_data;
196  int read = ffurl_read_complete(c->tls_shared.tcp, data, *dataLength);
197  if (read <= 0) {
198  *dataLength = 0;
199  switch(AVUNERROR(read)) {
200  case ENOENT:
201  case 0:
202  return errSSLClosedGraceful;
203  case ECONNRESET:
204  return errSSLClosedAbort;
205  case EAGAIN:
206  return errSSLWouldBlock;
207  default:
208  c->lastErr = read;
209  return ioErr;
210  }
211  } else {
212  *dataLength = read;
213  return noErr;
214  }
215 }
216 
217 static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
218 {
219  URLContext *h = (URLContext*)connection;
220  TLSContext *c = h->priv_data;
221  int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
222  if (written <= 0) {
223  *dataLength = 0;
224  switch(AVUNERROR(written)) {
225  case EAGAIN:
226  return errSSLWouldBlock;
227  default:
228  c->lastErr = written;
229  return ioErr;
230  }
231  } else {
232  *dataLength = written;
233  return noErr;
234  }
235 }
236 
237 static int tls_close(URLContext *h)
238 {
239  TLSContext *c = h->priv_data;
240  if (c->ssl_context) {
241  SSLClose(c->ssl_context);
242  CFRelease(c->ssl_context);
243  }
244  if (c->ca_array)
245  CFRelease(c->ca_array);
246  if (c->tls_shared.tcp)
248  return 0;
249 }
250 
251 #define CHECK_ERROR(func, ...) do { \
252  OSStatus status = func(__VA_ARGS__); \
253  if (status != noErr) { \
254  ret = AVERROR_UNKNOWN; \
255  av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
256  goto fail; \
257  } \
258  } while (0)
259 
260 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
261 {
262  TLSContext *c = h->priv_data;
263  TLSShared *s = &c->tls_shared;
264  int ret;
265 
266  if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
267  goto fail;
268 
269  c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
270  if (!c->ssl_context) {
271  av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
272  ret = AVERROR(ENOMEM);
273  goto fail;
274  }
275  if (s->ca_file) {
276  if ((ret = load_ca(h)) < 0)
277  goto fail;
278  }
279  if (s->ca_file || !s->verify)
280  CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
281  if (s->cert_file)
282  if ((ret = load_cert(h)) < 0)
283  goto fail;
284  CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
285  CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
286  CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
287  while (1) {
288  OSStatus status = SSLHandshake(c->ssl_context);
289  if (status == errSSLServerAuthCompleted) {
290  SecTrustRef peerTrust;
291  SecTrustResultType trustResult;
292  if (!s->verify)
293  continue;
294 
295  if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
296  ret = AVERROR(ENOMEM);
297  goto fail;
298  }
299 
300  if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
301  ret = AVERROR_UNKNOWN;
302  goto fail;
303  }
304 
305  if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
306  ret = AVERROR_UNKNOWN;
307  goto fail;
308  }
309 
310  if (trustResult == kSecTrustResultProceed ||
311  trustResult == kSecTrustResultUnspecified) {
312  // certificate is trusted
313  status = errSSLWouldBlock; // so we call SSLHandshake again
314  } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
315  // not trusted, for some reason other than being expired
316  status = errSSLXCertChainInvalid;
317  } else {
318  // cannot use this certificate (fatal)
319  status = errSSLBadCert;
320  }
321 
322  if (peerTrust)
323  CFRelease(peerTrust);
324  }
325  if (status == noErr)
326  break;
327 
328  av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
329  ret = AVERROR(EIO);
330  goto fail;
331  }
332 
333  return 0;
334 fail:
335  tls_close(h);
336  return ret;
337 }
338 
339 static int map_ssl_error(OSStatus status, size_t processed)
340 {
341  switch (status) {
342  case noErr:
343  return processed;
344  case errSSLClosedGraceful:
345  case errSSLClosedNoNotify:
346  return 0;
347  default:
348  return (int)status;
349  }
350 }
351 
352 static int tls_read(URLContext *h, uint8_t *buf, int size)
353 {
354  TLSContext *c = h->priv_data;
355  size_t processed = 0;
356  int ret = SSLRead(c->ssl_context, buf, size, &processed);
357  ret = map_ssl_error(ret, processed);
358  if (ret > 0)
359  return ret;
360  if (ret == 0)
361  return AVERROR_EOF;
362  return print_tls_error(h, ret);
363 }
364 
365 static int tls_write(URLContext *h, const uint8_t *buf, int size)
366 {
367  TLSContext *c = h->priv_data;
368  size_t processed = 0;
369  int ret = SSLWrite(c->ssl_context, buf, size, &processed);
370  ret = map_ssl_error(ret, processed);
371  if (ret > 0)
372  return ret;
373  if (ret == 0)
374  return AVERROR_EOF;
375  return print_tls_error(h, ret);
376 }
377 
379 {
380  TLSContext *c = h->priv_data;
382 }
383 
384 static const AVOption options[] = {
385  TLS_COMMON_OPTIONS(TLSContext, tls_shared),
386  { NULL }
387 };
388 
389 static const AVClass tls_class = {
390  .class_name = "tls",
391  .item_name = av_default_item_name,
392  .option = options,
393  .version = LIBAVUTIL_VERSION_INT,
394 };
395 
397  .name = "tls",
398  .url_open2 = tls_open,
399  .url_read = tls_read,
400  .url_write = tls_write,
401  .url_close = tls_close,
402  .url_get_file_handle = tls_get_file_handle,
403  .priv_data_size = sizeof(TLSContext),
405  .priv_data_class = &tls_class,
406 };
#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:334
#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:101
int verify
Definition: tls.h:33
#define LIBAVUTIL_VERSION_INT
Definition: version.h:86
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:419
AVIOInterruptCB interrupt_callback
Definition: url.h:47
#define AVIO_FLAG_READ
read-only
Definition: avio.h:660
int listen
Definition: tls.h:36
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)
AVOptions.
miscellaneous OS support macros and functions.
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:90
CFArrayRef ca_array
Definition: tls.h:31
static int flags
Definition: log.c:57
#define AVERROR_EOF
End of file.
Definition: error.h:55
ptrdiff_t size
Definition: opengl_enc.c:101
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:637
#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
av_default_item_name
#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:1112
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:109
char * host
Definition: tls.h:38
#define TLS_COMMON_OPTIONS(pstruct, options_field)
Definition: tls.h:47
char * cert_file
Definition: tls.h:34
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
Definition: avio.c:624
static int map_ssl_error(OSStatus status, size_t processed)
char * ca_file
Definition: tls.h:32
const char * protocol_blacklist
Definition: url.h:50
const URLProtocol ff_tls_securetransport_protocol
TLSShared tls_shared
Definition: tls_gnutls.c:50
void * buf
Definition: avisynth_c.h:690
Definition: url.h:38
GLint GLenum type
Definition: opengl_enc.c:105
static const char * format
Definition: movenc.c:47
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
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:1081
misc parsing utilities
const char * name
Definition: url.h:55
static const AVOption options[]
int ffurl_close(URLContext *h)
Definition: avio.c:465
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]
int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
Read as many bytes as possible (up to size), calling the read function multiple times if necessary...
Definition: avio.c:412
static int tls_write(URLContext *h, const uint8_t *buf, int size)
URLContext * tcp
Definition: tls.h:43
#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)
char * key_file
Definition: tls.h:35