3 * Copyright (c) 2011 Martin Storsjo
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "libavutil/avstring.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/parseutils.h"
28 #include "os_support.h"
31 #include <gnutls/gnutls.h>
32 #include <gnutls/x509.h>
33 #define TLS_read(c, buf, size) gnutls_record_recv(c->session, buf, size)
34 #define TLS_write(c, buf, size) gnutls_record_send(c->session, buf, size)
35 #define TLS_shutdown(c) gnutls_bye(c->session, GNUTLS_SHUT_RDWR)
36 #define TLS_free(c) do { \
38 gnutls_deinit(c->session); \
40 gnutls_certificate_free_credentials(c->cred); \
43 #include <openssl/bio.h>
44 #include <openssl/ssl.h>
45 #include <openssl/err.h>
46 #define TLS_read(c, buf, size) SSL_read(c->ssl, buf, size)
47 #define TLS_write(c, buf, size) SSL_write(c->ssl, buf, size)
48 #define TLS_shutdown(c) SSL_shutdown(c->ssl)
49 #define TLS_free(c) do { \
53 SSL_CTX_free(c->ctx); \
64 gnutls_session_t session
;
65 gnutls_certificate_credentials_t cred
;
78 #define OFFSET(x) offsetof(TLSContext, x)
79 #define D AV_OPT_FLAG_DECODING_PARAM
80 #define E AV_OPT_FLAG_ENCODING_PARAM
81 static const AVOption options
[] = {
82 {"ca_file", "Certificate Authority database file", OFFSET(ca_file
), AV_OPT_TYPE_STRING
, .flags
= D
|E
},
83 {"cafile", "Certificate Authority database file", OFFSET(ca_file
), AV_OPT_TYPE_STRING
, .flags
= D
|E
},
84 {"tls_verify", "Verify the peer certificate", OFFSET(verify
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, .flags
= D
|E
},
85 {"cert_file", "Certificate file", OFFSET(cert_file
), AV_OPT_TYPE_STRING
, .flags
= D
|E
},
86 {"key_file", "Private key file", OFFSET(key_file
), AV_OPT_TYPE_STRING
, .flags
= D
|E
},
87 {"listen", "Listen for incoming connections", OFFSET(listen
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, .flags
= D
|E
},
91 static const AVClass tls_class
= {
93 .item_name
= av_default_item_name
,
95 .version
= LIBAVUTIL_VERSION_INT
,
98 static int do_tls_poll(URLContext
*h
, int ret
)
100 TLSContext
*c
= h
->priv_data
;
101 struct pollfd p
= { c
->fd
, 0, 0 };
105 case GNUTLS_E_INTERRUPTED
:
107 case GNUTLS_E_WARNING_ALERT_RECEIVED
:
108 av_log(h
, AV_LOG_WARNING
, "%s\n", gnutls_strerror(ret
));
111 av_log(h
, AV_LOG_ERROR
, "%s\n", gnutls_strerror(ret
));
114 if (gnutls_record_get_direction(c
->session
))
119 ret
= SSL_get_error(c
->ssl
, ret
);
120 if (ret
== SSL_ERROR_WANT_READ
) {
122 } else if (ret
== SSL_ERROR_WANT_WRITE
) {
125 av_log(h
, AV_LOG_ERROR
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
129 if (h
->flags
& AVIO_FLAG_NONBLOCK
)
130 return AVERROR(EAGAIN
);
132 int n
= poll(&p
, 1, 100);
135 if (ff_check_interrupt(&h
->interrupt_callback
))
136 return AVERROR(EINTR
);
141 static void set_options(URLContext
*h
, const char *uri
)
143 TLSContext
*c
= h
->priv_data
;
145 const char *p
= strchr(uri
, '?');
149 if (!c
->ca_file
&& av_find_info_tag(buf
, sizeof(buf
), "cafile", p
))
150 c
->ca_file
= av_strdup(buf
);
152 if (!c
->verify
&& av_find_info_tag(buf
, sizeof(buf
), "verify", p
)) {
154 c
->verify
= strtol(buf
, &endptr
, 10);
159 if (!c
->cert_file
&& av_find_info_tag(buf
, sizeof(buf
), "cert", p
))
160 c
->cert_file
= av_strdup(buf
);
162 if (!c
->key_file
&& av_find_info_tag(buf
, sizeof(buf
), "key", p
))
163 c
->key_file
= av_strdup(buf
);
166 static int tls_open(URLContext
*h
, const char *uri
, int flags
)
168 TLSContext
*c
= h
->priv_data
;
172 char buf
[200], host
[200], opts
[50] = "";
174 struct addrinfo hints
= { 0 }, *ai
= NULL
;
175 const char *proxy_path
;
181 snprintf(opts
, sizeof(opts
), "?listen=1");
183 av_url_split(NULL
, 0, NULL
, 0, host
, sizeof(host
), &port
, NULL
, 0, uri
);
185 p
= strchr(uri
, '?');
190 if (av_find_info_tag(opts
, sizeof(opts
), "listen", p
))
194 ff_url_join(buf
, sizeof(buf
), "tcp", NULL
, host
, port
, "%s", p
);
196 hints
.ai_flags
= AI_NUMERICHOST
;
197 if (!getaddrinfo(host
, NULL
, &hints
, &ai
)) {
202 proxy_path
= getenv("http_proxy");
203 use_proxy
= !ff_http_match_no_proxy(getenv("no_proxy"), host
) &&
204 proxy_path
&& av_strstart(proxy_path
, "http://", NULL
);
207 char proxy_host
[200], proxy_auth
[200], dest
[200];
209 av_url_split(NULL
, 0, proxy_auth
, sizeof(proxy_auth
),
210 proxy_host
, sizeof(proxy_host
), &proxy_port
, NULL
, 0,
212 ff_url_join(dest
, sizeof(dest
), NULL
, NULL
, host
, port
, NULL
);
213 ff_url_join(buf
, sizeof(buf
), "httpproxy", proxy_auth
, proxy_host
,
214 proxy_port
, "/%s", dest
);
217 ret
= ffurl_open(&c
->tcp
, buf
, AVIO_FLAG_READ_WRITE
,
218 &h
->interrupt_callback
, NULL
);
221 c
->fd
= ffurl_get_file_handle(c
->tcp
);
224 gnutls_init(&c
->session
, c
->listen
? GNUTLS_SERVER
: GNUTLS_CLIENT
);
225 if (!c
->listen
&& !numerichost
)
226 gnutls_server_name_set(c
->session
, GNUTLS_NAME_DNS
, host
, strlen(host
));
227 gnutls_certificate_allocate_credentials(&c
->cred
);
230 ret
= gnutls_certificate_set_x509_trust_file(c
->cred
, c
->ca_file
, GNUTLS_X509_FMT_PEM
);
232 av_log(h
, AV_LOG_ERROR
, "%s\n", gnutls_strerror(ret
));
234 #if GNUTLS_VERSION_MAJOR >= 3
236 gnutls_certificate_set_x509_system_trust(c
->cred
);
238 gnutls_certificate_set_verify_flags(c
->cred
, c
->verify
?
239 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT
: 0);
240 if (c
->cert_file
&& c
->key_file
) {
241 ret
= gnutls_certificate_set_x509_key_file(c
->cred
,
242 c
->cert_file
, c
->key_file
,
243 GNUTLS_X509_FMT_PEM
);
245 av_log(h
, AV_LOG_ERROR
,
246 "Unable to set cert/key files %s and %s: %s\n",
247 c
->cert_file
, c
->key_file
, gnutls_strerror(ret
));
251 } else if (c
->cert_file
|| c
->key_file
)
252 av_log(h
, AV_LOG_ERROR
, "cert and key required\n");
253 gnutls_credentials_set(c
->session
, GNUTLS_CRD_CERTIFICATE
, c
->cred
);
254 gnutls_transport_set_ptr(c
->session
, (gnutls_transport_ptr_t
)
256 gnutls_priority_set_direct(c
->session
, "NORMAL", NULL
);
258 ret
= gnutls_handshake(c
->session
);
261 if ((ret
= do_tls_poll(h
, ret
)) < 0)
265 unsigned int status
, cert_list_size
;
266 gnutls_x509_crt_t cert
;
267 const gnutls_datum_t
*cert_list
;
268 if ((ret
= gnutls_certificate_verify_peers2(c
->session
, &status
)) < 0) {
269 av_log(h
, AV_LOG_ERROR
, "Unable to verify peer certificate: %s\n",
270 gnutls_strerror(ret
));
274 if (status
& GNUTLS_CERT_INVALID
) {
275 av_log(h
, AV_LOG_ERROR
, "Peer certificate failed verification\n");
279 if (gnutls_certificate_type_get(c
->session
) != GNUTLS_CRT_X509
) {
280 av_log(h
, AV_LOG_ERROR
, "Unsupported certificate type\n");
284 gnutls_x509_crt_init(&cert
);
285 cert_list
= gnutls_certificate_get_peers(c
->session
, &cert_list_size
);
286 gnutls_x509_crt_import(cert
, cert_list
, GNUTLS_X509_FMT_DER
);
287 ret
= gnutls_x509_crt_check_hostname(cert
, host
);
288 gnutls_x509_crt_deinit(cert
);
290 av_log(h
, AV_LOG_ERROR
,
291 "The certificate's owner does not match hostname %s\n", host
);
297 c
->ctx
= SSL_CTX_new(c
->listen
? TLSv1_server_method() : TLSv1_client_method());
299 av_log(h
, AV_LOG_ERROR
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
305 if (!SSL_CTX_load_verify_locations(c
->ctx
, c
->ca_file
, NULL
))
306 av_log(h
, AV_LOG_ERROR
, "SSL_CTX_load_verify_locations %s\n", ERR_error_string(ERR_get_error(), NULL
));
308 if (c
->cert_file
&& !SSL_CTX_use_certificate_chain_file(c
->ctx
, c
->cert_file
)) {
309 av_log(h
, AV_LOG_ERROR
, "Unable to load cert file %s: %s\n",
310 c
->cert_file
, ERR_error_string(ERR_get_error(), NULL
));
314 if (c
->key_file
&& !SSL_CTX_use_PrivateKey_file(c
->ctx
, c
->key_file
, SSL_FILETYPE_PEM
)) {
315 av_log(h
, AV_LOG_ERROR
, "Unable to load key file %s: %s\n",
316 c
->key_file
, ERR_error_string(ERR_get_error(), NULL
));
320 // Note, this doesn't check that the peer certificate actually matches
321 // the requested hostname.
323 SSL_CTX_set_verify(c
->ctx
, SSL_VERIFY_PEER
|SSL_VERIFY_FAIL_IF_NO_PEER_CERT
, NULL
);
324 c
->ssl
= SSL_new(c
->ctx
);
326 av_log(h
, AV_LOG_ERROR
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
330 SSL_set_fd(c
->ssl
, c
->fd
);
331 if (!c
->listen
&& !numerichost
)
332 SSL_set_tlsext_host_name(c
->ssl
, host
);
334 ret
= c
->listen
? SSL_accept(c
->ssl
) : SSL_connect(c
->ssl
);
338 av_log(h
, AV_LOG_ERROR
, "Unable to negotiate TLS/SSL session\n");
342 if ((ret
= do_tls_poll(h
, ret
)) < 0)
355 static int tls_read(URLContext
*h
, uint8_t *buf
, int size
)
357 TLSContext
*c
= h
->priv_data
;
359 int ret
= TLS_read(c
, buf
, size
);
364 if ((ret
= do_tls_poll(h
, ret
)) < 0)
370 static int tls_write(URLContext
*h
, const uint8_t *buf
, int size
)
372 TLSContext
*c
= h
->priv_data
;
374 int ret
= TLS_write(c
, buf
, size
);
379 if ((ret
= do_tls_poll(h
, ret
)) < 0)
385 static int tls_close(URLContext
*h
)
387 TLSContext
*c
= h
->priv_data
;
395 URLProtocol ff_tls_protocol
= {
397 .url_open
= tls_open
,
398 .url_read
= tls_read
,
399 .url_write
= tls_write
,
400 .url_close
= tls_close
,
401 .priv_data_size
= sizeof(TLSContext
),
402 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
403 .priv_data_class
= &tls_class
,