Commit f31c36e5 authored by Michael Niedermayer's avatar Michael Niedermayer

Merge commit '705b748e'

* commit '705b748e':
  tls: Add support for listen mode

Conflicts:
	doc/protocols.texi
	libavformat/tls.c
	libavformat/version.h

See: 4f4eb380Merged-by: 's avatarMichael Niedermayer <michaelni@gmx.at>
parents 689a1bd9 705b748e
...@@ -883,14 +883,18 @@ the host name is validated as well.) ...@@ -883,14 +883,18 @@ the host name is validated as well.)
This is disabled by default since it requires a CA database to be This is disabled by default since it requires a CA database to be
provided by the caller in many cases. provided by the caller in many cases.
@item listen @item cert_file, cert=@var{filename}
Act as a server, listening for an incoming connection. A file containing a certificate to use in the handshake with the peer.
(When operating as server, in listen mode, this is more often required
@item cert=@var{filename} by the peer, while client certificates only are mandated in certain
Certificate file. The file must be in OpenSSL PEM format. setups.)
@item key=@var{filename} @item key_file, key=@var{filename}
Private key file. A file containing the private key for the certificate.
@item listen=@var{1|0}
If enabled, listen for connections on the provided port, and assume
the server role in the handshake instead of the client role.
@end table @end table
......
...@@ -70,6 +70,9 @@ typedef struct { ...@@ -70,6 +70,9 @@ typedef struct {
int fd; int fd;
char *ca_file; char *ca_file;
int verify; int verify;
char *cert_file;
char *key_file;
int listen;
} TLSContext; } TLSContext;
#define OFFSET(x) offsetof(TLSContext, x) #define OFFSET(x) offsetof(TLSContext, x)
...@@ -79,6 +82,9 @@ static const AVOption options[] = { ...@@ -79,6 +82,9 @@ static const AVOption options[] = {
{"ca_file", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E }, {"ca_file", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E },
{"cafile", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E }, {"cafile", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E },
{"tls_verify", "Verify the peer certificate", OFFSET(verify), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E }, {"tls_verify", "Verify the peer certificate", OFFSET(verify), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
{"cert_file", "Certificate file", OFFSET(cert_file), AV_OPT_TYPE_STRING, .flags = D|E },
{"key_file", "Private key file", OFFSET(key_file), AV_OPT_TYPE_STRING, .flags = D|E },
{"listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
{ NULL } { NULL }
}; };
...@@ -135,11 +141,7 @@ static int do_tls_poll(URLContext *h, int ret) ...@@ -135,11 +141,7 @@ static int do_tls_poll(URLContext *h, int ret)
static void set_options(URLContext *h, const char *uri) static void set_options(URLContext *h, const char *uri)
{ {
TLSContext *c = h->priv_data; TLSContext *c = h->priv_data;
char buf[1024], key[1024]; char buf[1024];
int has_cert, has_key;
#if CONFIG_GNUTLS
int ret;
#endif
const char *p = strchr(uri, '?'); const char *p = strchr(uri, '?');
if (!p) if (!p)
return; return;
...@@ -154,22 +156,11 @@ static void set_options(URLContext *h, const char *uri) ...@@ -154,22 +156,11 @@ static void set_options(URLContext *h, const char *uri)
c->verify = 1; c->verify = 1;
} }
has_cert = av_find_info_tag(buf, sizeof(buf), "cert", p); if (!c->cert_file && av_find_info_tag(buf, sizeof(buf), "cert", p))
has_key = av_find_info_tag(key, sizeof(key), "key", p); c->cert_file = av_strdup(buf);
#if CONFIG_GNUTLS
if (has_cert && has_key) { if (!c->key_file && av_find_info_tag(buf, sizeof(buf), "key", p))
ret = gnutls_certificate_set_x509_key_file(c->cred, buf, key, GNUTLS_X509_FMT_PEM); c->key_file = av_strdup(buf);
if (ret < 0)
av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret));
} else if (has_cert ^ has_key) {
av_log(h, AV_LOG_ERROR, "cert and key required\n");
}
#elif CONFIG_OPENSSL
if (has_cert && !SSL_CTX_use_certificate_chain_file(c->ctx, buf))
av_log(h, AV_LOG_ERROR, "SSL_CTX_use_certificate_chain_file %s\n", ERR_error_string(ERR_get_error(), NULL));
if (has_key && !SSL_CTX_use_PrivateKey_file(c->ctx, key, SSL_FILETYPE_PEM))
av_log(h, AV_LOG_ERROR, "SSL_CTX_use_PrivateKey_file %s\n", ERR_error_string(ERR_get_error(), NULL));
#endif
} }
static int tls_open(URLContext *h, const char *uri, int flags) static int tls_open(URLContext *h, const char *uri, int flags)
...@@ -177,20 +168,22 @@ static int tls_open(URLContext *h, const char *uri, int flags) ...@@ -177,20 +168,22 @@ static int tls_open(URLContext *h, const char *uri, int flags)
TLSContext *c = h->priv_data; TLSContext *c = h->priv_data;
int ret; int ret;
int port; int port;
char buf[200], host[200], path[1024]; char buf[200], host[200], opts[50] = "";
int numerichost = 0; int numerichost = 0;
struct addrinfo hints = { 0 }, *ai = NULL; struct addrinfo hints = { 0 }, *ai = NULL;
const char *proxy_path; const char *proxy_path;
int use_proxy; int use_proxy;
int server = 0;
const char *p = strchr(uri, '?'); const char *p = strchr(uri, '?');
if (p && av_find_info_tag(buf, sizeof(buf), "listen", p))
server = 1;
ff_tls_init(); ff_tls_init();
av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, path, sizeof(path), uri); if(p && av_find_info_tag(buf, sizeof(buf), "listen", p))
ff_url_join(buf, sizeof(buf), "tcp", NULL, host, port, "%s", path); c->listen = 1;
if (c->listen)
snprintf(opts, sizeof(opts), "?listen=1");
av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, uri);
ff_url_join(buf, sizeof(buf), "tcp", NULL, host, port, "%s", opts);
hints.ai_flags = AI_NUMERICHOST; hints.ai_flags = AI_NUMERICHOST;
if (!getaddrinfo(host, NULL, &hints, &ai)) { if (!getaddrinfo(host, NULL, &hints, &ai)) {
...@@ -220,8 +213,8 @@ static int tls_open(URLContext *h, const char *uri, int flags) ...@@ -220,8 +213,8 @@ static int tls_open(URLContext *h, const char *uri, int flags)
c->fd = ffurl_get_file_handle(c->tcp); c->fd = ffurl_get_file_handle(c->tcp);
#if CONFIG_GNUTLS #if CONFIG_GNUTLS
gnutls_init(&c->session, server ? GNUTLS_SERVER : GNUTLS_CLIENT); gnutls_init(&c->session, c->listen ? GNUTLS_SERVER : GNUTLS_CLIENT);
if (!numerichost) if (!c->listen && !numerichost)
gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, host, strlen(host)); gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, host, strlen(host));
gnutls_certificate_allocate_credentials(&c->cred); gnutls_certificate_allocate_credentials(&c->cred);
set_options(h, uri); set_options(h, uri);
...@@ -236,6 +229,19 @@ static int tls_open(URLContext *h, const char *uri, int flags) ...@@ -236,6 +229,19 @@ static int tls_open(URLContext *h, const char *uri, int flags)
#endif #endif
gnutls_certificate_set_verify_flags(c->cred, c->verify ? gnutls_certificate_set_verify_flags(c->cred, c->verify ?
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT : 0); GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT : 0);
if (c->cert_file && c->key_file) {
ret = gnutls_certificate_set_x509_key_file(c->cred,
c->cert_file, c->key_file,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
av_log(h, AV_LOG_ERROR,
"Unable to set cert/key files %s and %s: %s\n",
c->cert_file, c->key_file, gnutls_strerror(ret));
ret = AVERROR(EIO);
goto fail;
}
} else if (c->cert_file || c->key_file) {
av_log(h, AV_LOG_ERROR, "cert and key required\n");
gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred); gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred);
gnutls_transport_set_ptr(c->session, (gnutls_transport_ptr_t) gnutls_transport_set_ptr(c->session, (gnutls_transport_ptr_t)
(intptr_t) c->fd); (intptr_t) c->fd);
...@@ -280,7 +286,7 @@ static int tls_open(URLContext *h, const char *uri, int flags) ...@@ -280,7 +286,7 @@ static int tls_open(URLContext *h, const char *uri, int flags)
} }
} }
#elif CONFIG_OPENSSL #elif CONFIG_OPENSSL
c->ctx = SSL_CTX_new(server ? TLSv1_server_method() : TLSv1_client_method()); c->ctx = SSL_CTX_new(c->listen ? TLSv1_server_method() : TLSv1_client_method());
if (!c->ctx) { if (!c->ctx) {
av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL)); av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
ret = AVERROR(EIO); ret = AVERROR(EIO);
...@@ -291,6 +297,18 @@ static int tls_open(URLContext *h, const char *uri, int flags) ...@@ -291,6 +297,18 @@ static int tls_open(URLContext *h, const char *uri, int flags)
if (!SSL_CTX_load_verify_locations(c->ctx, c->ca_file, NULL)) if (!SSL_CTX_load_verify_locations(c->ctx, c->ca_file, NULL))
av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", ERR_error_string(ERR_get_error(), NULL)); av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", ERR_error_string(ERR_get_error(), NULL));
} }
if (c->cert_file && !SSL_CTX_use_certificate_chain_file(c->ctx, c->cert_file)) {
av_log(h, AV_LOG_ERROR, "Unable to load cert file %s: %s\n",
c->cert_file, ERR_error_string(ERR_get_error(), NULL));
ret = AVERROR(EIO);
goto fail;
}
if (c->key_file && !SSL_CTX_use_PrivateKey_file(c->ctx, c->key_file, SSL_FILETYPE_PEM)) {
av_log(h, AV_LOG_ERROR, "Unable to load key file %s: %s\n",
c->key_file, ERR_error_string(ERR_get_error(), NULL));
ret = AVERROR(EIO);
goto fail;
}
// Note, this doesn't check that the peer certificate actually matches // Note, this doesn't check that the peer certificate actually matches
// the requested hostname. // the requested hostname.
if (c->verify) if (c->verify)
...@@ -302,10 +320,10 @@ static int tls_open(URLContext *h, const char *uri, int flags) ...@@ -302,10 +320,10 @@ static int tls_open(URLContext *h, const char *uri, int flags)
goto fail; goto fail;
} }
SSL_set_fd(c->ssl, c->fd); SSL_set_fd(c->ssl, c->fd);
if (!server && !numerichost) if (!c->listen && !numerichost)
SSL_set_tlsext_host_name(c->ssl, host); SSL_set_tlsext_host_name(c->ssl, host);
while (1) { while (1) {
ret = server ? SSL_accept(c->ssl) : SSL_connect(c->ssl); ret = c->listen ? SSL_accept(c->ssl) : SSL_connect(c->ssl);
if (ret > 0) if (ret > 0)
break; break;
if (ret == 0) { if (ret == 0) {
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 55 #define LIBAVFORMAT_VERSION_MAJOR 55
#define LIBAVFORMAT_VERSION_MINOR 18 #define LIBAVFORMAT_VERSION_MINOR 18
#define LIBAVFORMAT_VERSION_MICRO 103 #define LIBAVFORMAT_VERSION_MICRO 104
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \ LIBAVFORMAT_VERSION_MINOR, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment