From 387ee1d6aa651e07e95ffe00ca4bfd14873613ad Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 4 Nov 2017 15:45:16 +0100 Subject: libavformat: LibreSSL (libtls) support Signed-off-by: sfan5 --- libavformat/Makefile | 1 + libavformat/tls_libtls.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 libavformat/tls_libtls.c (limited to 'libavformat') diff --git a/libavformat/Makefile b/libavformat/Makefile index 734b703862..cb70eac920 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -593,6 +593,7 @@ OBJS-$(CONFIG_SUBFILE_PROTOCOL) += subfile.o OBJS-$(CONFIG_TEE_PROTOCOL) += teeproto.o tee_common.o OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o TLS-OBJS-$(CONFIG_GNUTLS) += tls_gnutls.o +TLS-OBJS-$(CONFIG_LIBTLS) += tls_libtls.o TLS-OBJS-$(CONFIG_OPENSSL) += tls_openssl.o TLS-OBJS-$(CONFIG_SECURETRANSPORT) += tls_securetransport.o TLS-OBJS-$(CONFIG_SCHANNEL) += tls_schannel.o diff --git a/libavformat/tls_libtls.c b/libavformat/tls_libtls.c new file mode 100644 index 0000000000..1321f79229 --- /dev/null +++ b/libavformat/tls_libtls.c @@ -0,0 +1,207 @@ +/* + * TLS/SSL Protocol + * Copyright (c) 2011 Martin Storsjo + * Copyright (c) 2017 sfan5 + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "network.h" +#include "url.h" +#include "tls.h" +#include "libavcodec/internal.h" +#include "libavutil/avutil.h" +#include "libavutil/opt.h" + +#include + +typedef struct TLSContext { + const AVClass *class; + TLSShared tls_shared; + struct tls *ctx; +} TLSContext; + +static int ff_tls_close(URLContext *h) +{ + TLSContext *p = h->priv_data; + if (p->ctx) { + tls_close(p->ctx); + tls_free(p->ctx); + } + if (p->tls_shared.tcp) + ffurl_close(p->tls_shared.tcp); + return 0; +} + +static ssize_t tls_read_callback(struct tls *ctx, void *buf, size_t buflen, void *cb_arg) +{ + URLContext *h = (URLContext*) cb_arg; + int ret = ffurl_read(h, buf, buflen); + if (ret == AVERROR(EAGAIN)) + return TLS_WANT_POLLIN; + else if (ret == AVERROR_EXIT) + return 0; + return ret >= 0 ? ret : -1; +} + +static ssize_t tls_write_callback(struct tls *ctx, const void *buf, size_t buflen, void *cb_arg) +{ + URLContext *h = (URLContext*) cb_arg; + int ret = ffurl_write(h, buf, buflen); + if (ret == AVERROR(EAGAIN)) + return TLS_WANT_POLLOUT; + else if (ret == AVERROR_EXIT) + return 0; + return ret >= 0 ? ret : -1; +} + +static int ff_tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options) +{ + TLSContext *p = h->priv_data; + TLSShared *c = &p->tls_shared; + struct tls_config *cfg = NULL; + int ret; + + if (tls_init() == -1) { + ret = AVERROR(EIO); + goto fail; + } + + if ((ret = ff_tls_open_underlying(c, h, uri, options)) < 0) + goto fail; + + p->ctx = !c->listen ? tls_client() : tls_server(); + if (!p->ctx) { + ret = AVERROR(EIO); + goto fail; + } + + cfg = tls_config_new(); + if (!p->ctx) { + ret = AVERROR(EIO); + goto fail; + } + if (tls_config_set_protocols(cfg, TLS_PROTOCOLS_ALL) == -1) + goto err_config; + // While TLSv1.0 and TLSv1.1 are already enabled by the above, + // we need to be less strict with ciphers so it works in practice. + if (tls_config_set_ciphers(cfg, "compat") == -1) + goto err_config; + if (c->ca_file && tls_config_set_ca_file(cfg, c->ca_file) == -1) + goto err_config; + if (c->cert_file && tls_config_set_cert_file(cfg, c->cert_file) == -1) + goto err_config; + if (c->key_file && tls_config_set_key_file(cfg, c->key_file) == -1) + goto err_config; + if (!c->verify) { + tls_config_insecure_noverifycert(cfg); + tls_config_insecure_noverifyname(cfg); + tls_config_insecure_noverifytime(cfg); + } + if (tls_configure(p->ctx, cfg) == -1) + goto err_ctx; + + if (!c->listen) { + ret = tls_connect_cbs(p->ctx, tls_read_callback, tls_write_callback, + c->tcp, !c->numerichost ? c->host : NULL); + } else { + struct tls *ctx_new; + ret = tls_accept_cbs(p->ctx, &ctx_new, tls_read_callback, + tls_write_callback, c->tcp); + if (ret == 0) { + // free "server" context and replace by "connection" context + tls_free(p->ctx); + p->ctx = ctx_new; + } + } + if (ret == -1) + goto err_ctx; + + tls_config_free(cfg); + return 0; +err_config: + av_log(h, AV_LOG_ERROR, "%s\n", tls_config_error(cfg)); + ret = AVERROR(EIO); + goto fail; +err_ctx: + av_log(h, AV_LOG_ERROR, "%s\n", tls_error(p->ctx)); + ret = AVERROR(EIO); + /* fallthrough */ +fail: + if (cfg) + tls_config_free(cfg); + ff_tls_close(h); + return ret; +} + +static int ff_tls_read(URLContext *h, uint8_t *buf, int size) +{ + TLSContext *p = h->priv_data; + ssize_t ret; + ret = tls_read(p->ctx, buf, size); + if (ret > 0) + return ret; + else if (ret == 0) + return AVERROR_EOF; + av_log(h, AV_LOG_ERROR, "%s\n", tls_error(p->ctx)); + return AVERROR(EIO); +} + +static int ff_tls_write(URLContext *h, const uint8_t *buf, int size) +{ + TLSContext *p = h->priv_data; + ssize_t ret; + ret = tls_write(p->ctx, buf, size); + if (ret > 0) + return ret; + else if (ret == 0) + return AVERROR_EOF; + av_log(h, AV_LOG_ERROR, "%s\n", tls_error(p->ctx)); + return AVERROR(EIO); +} + +static int tls_get_file_handle(URLContext *h) +{ + TLSContext *c = h->priv_data; + return ffurl_get_file_handle(c->tls_shared.tcp); +} + +static const AVOption options[] = { + TLS_COMMON_OPTIONS(TLSContext, tls_shared), + { NULL } +}; + +static const AVClass tls_class = { + .class_name = "tls", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const URLProtocol ff_tls_protocol = { + .name = "tls", + .url_open2 = ff_tls_open, + .url_read = ff_tls_read, + .url_write = ff_tls_write, + .url_close = ff_tls_close, + .url_get_file_handle = tls_get_file_handle, + .priv_data_size = sizeof(TLSContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, + .priv_data_class = &tls_class, +}; -- cgit v1.2.3