PR #25472 opened by Arroyo Perez (SrRapero720)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25472
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25472.patch

# Summary
Make the caller's TLS settings (`tls_verify`, `ca_file`) apply to every HTTPS 
connection, including the ones HLS and DASH open for playlists, segments and 
keys.

**Problem**
These are not http options today: they only reach the tls protocol as leftover 
entries of the connection they were passed to. `ffio_copy_url_options()` cannot 
read them, since the http class has no `child_next` and the tls context is 
unreachable, so nested connections fall back to the defaults. They verify 
against the system store even when the caller set `tls_verify=0` or gave a 
`ca_file`, and a stream from a self-signed or private-CA server fails after the 
first playlist.

**Changes**
- `avformat/http: pass ca_file and tls_verify to every TLS connection`: add the 
options (with the `cafile`/`verify` aliases of tls) to http and forward them on 
every https connection, including redirects, seeks and reconnections.
- `avformat/aviobuf: copy tls_verify and ca_file to child connections`: HLS, 
DASH and IMF now pass them on to nested requests.
- `avformat/hls, dashdec: add protocol_opts for nested requests`: lets the 
caller seed nested requests when there is nothing to copy (local playlist, 
custom AVIOContext).

Documentation is updated in `doc/protocols.texi` and `doc/demuxers.texi`.

Related: #24809 adds the same names to the copy list in `aviobuf.c`, but on its 
own only takes effect for libcurl. Both PRs touch the same line.

Assisted with Claude Opus 5.5


>From 7e6fbb6485e2254973cc6b2810781c0fd7854dff Mon Sep 17 00:00:00 2001
From: J-RAP <[email protected]>
Date: Mon, 26 Oct 2026 03:17:05 -0600
Subject: [PATCH 1/3] avformat/http: pass ca_file and tls_verify to every TLS
 connection

The tls protocol verifies the peer by default, but the http protocol
does not know ca_file and tls_verify. Given for an https URL, they are
left over in the options dictionary, which http keeps in
chained_options and hands to the tls protocol. They are therefore not
visible on the http context: ffio_copy_url_options() reads options with
AV_OPT_SEARCH_CHILDREN, and the http class has no child_next, so the
tls URLContext that holds them cannot be reached. A demuxer such as hls
or dash that opens further connections cannot find out which CA file
or verification setting the caller asked for.

Make them options of the http protocol, together with the cafile and
verify aliases of the tls protocol, so that a leftover alias cannot
override the value http forwards. Since they are no longer left in
chained_options, set them in the dictionary given to the tls protocol
for every https connection, including the ones opened for redirects,
seeks and reconnections, and for https in listen mode.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: J-RAP <[email protected]>
---
 doc/protocols.texi | 10 ++++++++++
 libavformat/http.c | 24 +++++++++++++++++++++++-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index aaee2292e9d..07ef0998096 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -472,6 +472,16 @@ If set to 1 use chunked Transfer-Encoding for posts, 
default is 1.
 @item http_proxy
 set HTTP proxy to tunnel through e.g. http://example.com:1234
 
+@item tls_verify, verify
+Verify the peer certificate and host name. Enabled by default.
+
+@item ca_file, cafile
+Path to a certificate authority bundle that overrides the system store.
+
+@option{tls_verify} and @option{ca_file} are passed to the @code{tls}
+protocol for every @code{https} connection, including the ones opened for
+redirects, seeks and reconnections.
+
 @item headers
 Set custom HTTP headers, can override built in default headers. The
 value must be a string encoding the headers.
diff --git a/libavformat/http.c b/libavformat/http.c
index 2f4f522363a..5fb2d59a6d5 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -79,6 +79,8 @@ typedef struct HTTPContext {
     uint64_t off, end_off; /* `off` is also mutated by seeking / reading */
     char *location;
     char *http_proxy;
+    char *ca_file;
+    int tls_verify;
     char *headers;
     char *mime_type;
     char *http_version;
@@ -195,6 +197,10 @@ static const AVOption http_options[] = {
     { "seekable", "control seekability of connection", OFFSET(seekable), 
AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D },
     { "chunked_post", "use chunked transfer-encoding for posts", 
OFFSET(chunked_post), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
     { "http_proxy", "set HTTP proxy to tunnel through", OFFSET(http_proxy), 
AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
+    { "ca_file", "set the Certificate Authority database file for https", 
OFFSET(ca_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
+    { "cafile", "set the Certificate Authority database file for https", 
OFFSET(ca_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
+    { "tls_verify", "verify the peer certificate for https", 
OFFSET(tls_verify), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D | E },
+    { "verify", "verify the peer certificate for https", OFFSET(tls_verify), 
AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D | E },
     { "headers", "set custom HTTP headers, can override built in default 
headers", OFFSET(headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
     { "content_type", "set a specific content type for the POST messages", 
OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
     { "user_agent", "override User-Agent header", OFFSET(user_agent), 
AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
@@ -252,6 +258,16 @@ void ff_http_init_auth_state(URLContext *dest, const 
URLContext *src)
            sizeof(HTTPAuthState));
 }
 
+/* The TLS options belong to this context, so they are not left in
+ * chained_options and have to be passed to every TLS connection. */
+static int http_tls_options(HTTPContext *s, AVDictionary **options)
+{
+    int ret = av_dict_set_int(options, "tls_verify", s->tls_verify, 0);
+    if (ret < 0)
+        return ret;
+    return av_dict_set(options, "ca_file", s->ca_file, 0);
+}
+
 static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
 {
     const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
@@ -304,6 +320,9 @@ static int http_open_cnx_internal(URLContext *h, 
AVDictionary **options)
         use_proxy   = 0;
         if (port < 0)
             port = 443;
+        err = http_tls_options(s, options);
+        if (err < 0)
+            goto end;
         /* pass http_proxy to underlying protocol */
         if (s->http_proxy) {
             err = av_dict_set(options, "http_proxy", s->http_proxy, 0);
@@ -792,8 +811,11 @@ static int http_listen(URLContext *h, const char *uri, int 
flags,
     int port;
     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), 
&port,
                  NULL, 0, uri);
-    if (!strcmp(proto, "https"))
+    if (!strcmp(proto, "https")) {
         lower_proto = "tls";
+        if ((ret = http_tls_options(s, options)) < 0)
+            goto fail;
+    }
     ff_url_join(lower_url, sizeof(lower_url), lower_proto, NULL, hostname, 
port,
                 NULL);
     if ((ret = av_dict_set_int(options, "listen", s->listen, 0)) < 0)
-- 
2.54.0


>From e85059ffd0df16935db97005bf3291133f23d9a5 Mon Sep 17 00:00:00 2001
From: J-RAP <[email protected]>
Date: Mon, 26 Oct 2026 03:17:47 -0600
Subject: [PATCH 2/3] avformat/aviobuf: copy tls_verify and ca_file to child
 connections

The hls, dash and imf demuxers open playlists, segments and keys with
the options that ffio_copy_url_options() reads from their input. As
tls_verify and ca_file were not in the list, those connections verified
the peer against the default certificate store even if the caller gave
a CA file, and kept verifying it if the caller disabled verification.

Copy them too. With the previous commit, they can be read from the http
protocol; the libcurl protocol has options with the same names.

This complements https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24809,
which adds the same names (and cert_file and key_file) to the list, but
which on its own only takes effect for the libcurl protocol, as the
native http protocol did not have these options.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: J-RAP <[email protected]>
---
 doc/protocols.texi    | 3 ++-
 libavformat/aviobuf.c | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 07ef0998096..9e9497fdfcd 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -480,7 +480,8 @@ Path to a certificate authority bundle that overrides the 
system store.
 
 @option{tls_verify} and @option{ca_file} are passed to the @code{tls}
 protocol for every @code{https} connection, including the ones opened for
-redirects, seeks and reconnections.
+redirects, seeks and reconnections. They are also copied to the connections
+that demuxers such as HLS and DASH open for playlists, segments and keys.
 
 @item headers
 Set custom HTTP headers, can override built in default headers. The
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index 43431381b9b..a4fd0fbbeb0 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -962,7 +962,8 @@ void ffio_write_lines(AVIOContext *s, const unsigned char 
*buf, int size,
 int ffio_copy_url_options(AVIOContext* pb, AVDictionary** avio_opts)
 {
     const char *opts[] = {
-        "headers", "user_agent", "cookies", "http_proxy", "referer", 
"rw_timeout", "icy", "prefer_libcurl", NULL };
+        "headers", "user_agent", "cookies", "http_proxy", "referer", 
"rw_timeout", "icy", "prefer_libcurl",
+        "tls_verify", "ca_file", NULL };
     const char **opt = opts;
     uint8_t *buf = NULL;
     int ret = 0;
-- 
2.54.0


>From fb089e48e2e146a06adf086f00a1df57273e999c Mon Sep 17 00:00:00 2001
From: J-RAP <[email protected]>
Date: Mon, 26 Oct 2026 03:18:33 -0600
Subject: [PATCH 3/3] avformat/hls, dashdec: add protocol_opts for nested
 requests

The options for the requests that these demuxers open for playlists,
segments and keys are only copied from the connection of the playlist
with ffio_copy_url_options(). When the playlist is not read over HTTP,
for example from a local file or a custom AVIOContext, nothing can be
copied, and a caller has no way to give the nested requests options
such as tls_verify or ca_file.

Expose the dictionary used for these requests as the protocol_opts
option, so that a caller can seed it. Options copied from the
connection of the playlist, or set by the demuxer itself, still take
precedence.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: J-RAP <[email protected]>
---
 doc/demuxers.texi     | 18 ++++++++++++++++++
 libavformat/dashdec.c |  2 ++
 libavformat/hls.c     |  2 ++
 3 files changed, 22 insertions(+)

diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index bc8116d9d23..eee5ca0fc14 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -303,6 +303,15 @@ Default 16-byte key, in hex, to decrypt files encrypted 
using ISO Common Encrypt
 Dictionary of 16-byte key ID => 16-byte key, both in hex, to decrypt files 
encrypted using ISO Common Encryption
 (CENC/AES-128 CTR; ISO/IEC 23001-7).
 
+@item protocol_opts
+Set options for the protocol of the nested requests for manifests and segments,
+using a list of key=value pairs separated by @code{:}, for example
+@code{tls_verify=0} or @code{ca_file=@var{ca.pem}}. This is useful when the
+manifest itself is not read over HTTP, for example from a local file or a
+custom I/O context, so that no options can be copied from its connection.
+Options that the demuxer copies from that connection, or sets itself, take
+precedence.
+
 @end table
 
 @section dvdvideo
@@ -616,6 +625,15 @@ Use HTTP partial requests for downloading HTTP segments.
 @item seg_format_options
 Set options for the demuxer of media segments using a list of key=value pairs 
separated by @code{:}.
 
+@item protocol_opts
+Set options for the protocol of the nested requests for playlists, segments and
+keys, using a list of key=value pairs separated by @code{:}, for example
+@code{tls_verify=0} or @code{ca_file=@var{ca.pem}}. This is useful when the
+playlist itself is not read over HTTP, for example from a local file or a
+custom I/O context, so that no options can be copied from its connection.
+Options that the demuxer copies from that connection, or sets itself, take
+precedence.
+
 @item seg_max_retry
 Maximum number of times to reload a segment on error, useful when segment skip 
on network error is not desired.
 Default value is 0.
diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
index 68ccb73bd0f..743e99f69a5 100644
--- a/libavformat/dashdec.c
+++ b/libavformat/dashdec.c
@@ -2580,6 +2580,8 @@ static const AVOption dash_options[] = {
     { "cenc_decryption_keys", "Media decryption keys by KID (hex)", 
OFFSET(cenc_decryption_keys), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, 
INT_MAX, .flags = FLAGS },
     { "max_reload", "Maximum number of manifest reloads in 
get_current_fragment() before giving up",
         OFFSET(max_reload), AV_OPT_TYPE_INT, { .i64 = 100 }, 0, INT_MAX, FLAGS 
},
+    {"protocol_opts", "Set options for nested protocol requests",
+        OFFSET(avio_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
     {NULL}
 };
 
diff --git a/libavformat/hls.c b/libavformat/hls.c
index c68b5e88cf5..bf7e7a3511c 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -3350,6 +3350,8 @@ static const AVOption hls_options[] = {
         OFFSET(http_seekable), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, FLAGS},
     {"seg_format_options", "Set options for segment demuxer",
         OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
+    {"protocol_opts", "Set options for nested protocol requests",
+        OFFSET(avio_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
     {"seg_max_retry", "Maximum number of times to reload a segment on error.",
      OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS},
     {NULL}
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to