[FFmpeg-devel] [PATCH 2/3] avformat: add protocol_whitelist

Michael Niedermayer michaelni at gmx.at
Thu Jan 28 23:49:07 CET 2016


From: Michael Niedermayer <michael at niedermayer.cc>

TODO: Docs
TODO: version bump

Note to maintainers: update tools

Note to maintainers: set a default whitelist for your protocol
If that makes no sense then consider to set "none" and thus require the user to specify a white-list
for sub-protocols to be opened

Note, testing and checking for missing changes is needed

Signed-off-by: Michael Niedermayer <michael at niedermayer.cc>
---
 libavformat/avformat.h      |    7 +++++
 libavformat/avio.c          |   66 ++++++++++++++++++++++++++++++++++++++++---
 libavformat/avio.h          |    5 ++++
 libavformat/avio_internal.h |    4 +++
 libavformat/aviobuf.c       |   25 +++++++++++++---
 libavformat/options_table.h |    1 +
 libavformat/url.h           |    6 ++++
 libavformat/utils.c         |   22 ++++++++++++---
 8 files changed, 124 insertions(+), 12 deletions(-)

diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 285bb16..273a6ae 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -1827,6 +1827,13 @@ typedef struct AVFormatContext {
      * Demuxing: Set by user.
      */
     int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+    /**
+     * ',' separated list of allowed protocols.
+     * - encoding: unused
+     * - decoding: set by user through AVOptions (NO direct access)
+     */
+    char *protocol_whitelist;
 } AVFormatContext;
 
 int av_format_get_probe_score(const AVFormatContext *s);
diff --git a/libavformat/avio.c b/libavformat/avio.c
index 96b18fd..c5c6bb1 100644
--- a/libavformat/avio.c
+++ b/libavformat/avio.c
@@ -73,7 +73,13 @@ static const AVClass *urlcontext_child_class_next(const AVClass *prev)
     return NULL;
 }
 
-static const AVOption options[] = { { NULL } };
+#define OFFSET(x) offsetof(URLContext,x)
+#define E AV_OPT_FLAG_ENCODING_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  CHAR_MIN, CHAR_MAX, D },
+    { NULL }
+};
 const AVClass ffurl_context_class = {
     .class_name       = "URLContext",
     .item_name        = urlcontext_to_name,
@@ -201,12 +207,42 @@ fail:
 
 int ffurl_connect(URLContext *uc, AVDictionary **options)
 {
-    int err =
+    int err;
+    AVDictionary *tmp_opts = NULL;
+    AVDictionaryEntry *e;
+
+    if (!options)
+        options = &tmp_opts;
+
+    // Check that URLContext was initialized correctly and lists are matching if set
+    av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
+               !strcmp(uc->protocol_whitelist, e->value));
+
+    if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {
+        av_log(uc, AV_LOG_ERROR, "Protocol not on whitelist \'%s\'!\n", uc->protocol_whitelist);
+        return AVERROR(EINVAL);
+    }
+
+    if (!uc->protocol_whitelist && uc->prot->default_whitelist) {
+        uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist);
+        if (!uc->protocol_whitelist) {
+            return AVERROR(ENOMEM);
+        }
+    } else if (!uc->protocol_whitelist)
+        av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist
+
+    if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0)
+        return err;
+
+    err =
         uc->prot->url_open2 ? uc->prot->url_open2(uc,
                                                   uc->filename,
                                                   uc->flags,
                                                   options) :
         uc->prot->url_open(uc, uc->filename, uc->flags);
+
+    av_dict_set(options, "protocol_whitelist", NULL, 0);
+
     if (err)
         return err;
     uc->is_connected = 1;
@@ -296,18 +332,33 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags,
     return AVERROR_PROTOCOL_NOT_FOUND;
 }
 
-int ffurl_open(URLContext **puc, const char *filename, int flags,
-               const AVIOInterruptCB *int_cb, AVDictionary **options)
+int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
+                         const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist)
 {
+    AVDictionary *tmp_opts = NULL;
+    AVDictionaryEntry *e;
     int ret = ffurl_alloc(puc, filename, flags, int_cb);
     if (ret < 0)
         return ret;
     if (options && (*puc)->prot->priv_data_class &&
         (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
         goto fail;
+
+    if (!options)
+        options = &tmp_opts;
+
+    av_assert0(!whitelist ||
+               !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
+               !strcmp(whitelist, e->value));
+
+    if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
+        goto fail;
+
     if ((ret = av_opt_set_dict(*puc, options)) < 0)
         goto fail;
+
     ret = ffurl_connect(*puc, options);
+
     if (!ret)
         return 0;
 fail:
@@ -316,6 +367,13 @@ fail:
     return ret;
 }
 
+int ffurl_open(URLContext **puc, const char *filename, int flags,
+               const AVIOInterruptCB *int_cb, AVDictionary **options)
+{
+    return ffurl_open_whitelist(puc, filename, flags,
+                                int_cb, options, NULL);
+}
+
 static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
                                          int size, int size_min,
                                          int (*transfer_func)(URLContext *h,
diff --git a/libavformat/avio.h b/libavformat/avio.h
index c3c0b73..7fbce32 100644
--- a/libavformat/avio.h
+++ b/libavformat/avio.h
@@ -249,6 +249,11 @@ typedef struct AVIOContext {
      * This is current internal only, do not use from outside.
      */
     int short_seek_threshold;
+
+    /**
+     * ',' separated list of allowed protocols.
+     */
+    const char *protocol_whitelist;
 } AVIOContext;
 
 /* unbuffered I/O */
diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h
index ad50567..f7c8588 100644
--- a/libavformat/avio_internal.h
+++ b/libavformat/avio_internal.h
@@ -149,6 +149,10 @@ int ffio_fdopen(AVIOContext **s, URLContext *h);
  */
 int ffio_open_null_buf(AVIOContext **s);
 
+int ffio_open_whitelist(AVIOContext **s, const char *url, int flags,
+                         const AVIOInterruptCB *int_cb, AVDictionary **options,
+                         const char *whitelist);
+
 /**
  * Close a null buffer.
  *
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index 9851981..213ee96 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -53,7 +53,11 @@ static const AVClass *ff_avio_child_class_next(const AVClass *prev)
     return prev ? NULL : &ffurl_context_class;
 }
 
+#define OFFSET(x) offsetof(AVIOContext,x)
+#define E AV_OPT_FLAG_ENCODING_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
 static const AVOption ff_avio_options[] = {
+    {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  CHAR_MIN, CHAR_MAX, D },
     { NULL },
 };
 
@@ -800,6 +804,11 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
         av_free(buffer);
         return AVERROR(ENOMEM);
     }
+    (*s)->protocol_whitelist = av_strdup(h->protocol_whitelist);
+    if (!(*s)->protocol_whitelist && h->protocol_whitelist) {
+        avio_closep(s);
+        return AVERROR(ENOMEM);
+    }
     (*s)->direct = h->flags & AVIO_FLAG_DIRECT;
     (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
     (*s)->max_packet_size = max_packet_size;
@@ -919,13 +928,15 @@ int avio_open(AVIOContext **s, const char *filename, int flags)
     return avio_open2(s, filename, flags, NULL, NULL);
 }
 
-int avio_open2(AVIOContext **s, const char *filename, int flags,
-               const AVIOInterruptCB *int_cb, AVDictionary **options)
+int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
+                         const AVIOInterruptCB *int_cb, AVDictionary **options,
+                         const char *whitelist
+                        )
 {
     URLContext *h;
     int err;
 
-    err = ffurl_open(&h, filename, flags, int_cb, options);
+    err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist);
     if (err < 0)
         return err;
     err = ffio_fdopen(s, h);
@@ -936,10 +947,16 @@ int avio_open2(AVIOContext **s, const char *filename, int flags,
     return 0;
 }
 
+int avio_open2(AVIOContext **s, const char *filename, int flags,
+               const AVIOInterruptCB *int_cb, AVDictionary **options)
+{
+    return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL);
+}
+
 int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
                        const AVIOInterruptCB *int_cb, AVDictionary **options)
 {
-    return avio_open2(pb, url, flags, int_cb, options);
+    return ffio_open_whitelist(pb, url, flags, int_cb, options, s->protocol_whitelist);
 }
 
 int avio_close(AVIOContext *s)
diff --git a/libavformat/options_table.h b/libavformat/options_table.h
index cc64bea..8926fe5 100644
--- a/libavformat/options_table.h
+++ b/libavformat/options_table.h
@@ -100,6 +100,7 @@ static const AVOption avformat_options[] = {
 {"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = ", "}, CHAR_MIN, CHAR_MAX, D|E},
 {"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  CHAR_MIN, CHAR_MAX, D },
 {"format_whitelist", "List of demuxers that are allowed to be used", OFFSET(format_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  CHAR_MIN, CHAR_MAX, D },
+{"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  CHAR_MIN, CHAR_MAX, D },
 {NULL},
 };
 
diff --git a/libavformat/url.h b/libavformat/url.h
index 391e3bc..eb950f3 100644
--- a/libavformat/url.h
+++ b/libavformat/url.h
@@ -47,6 +47,7 @@ typedef struct URLContext {
     int is_connected;
     AVIOInterruptCB interrupt_callback;
     int64_t rw_timeout;         /**< maximum time to wait for (network) read/write operation completion, in mcs */
+    const char *protocol_whitelist;
 } URLContext;
 
 typedef struct URLProtocol {
@@ -94,6 +95,7 @@ typedef struct URLProtocol {
     int (*url_close_dir)(URLContext *h);
     int (*url_delete)(URLContext *h);
     int (*url_move)(URLContext *h_src, URLContext *h_dst);
+    char *default_whitelist;
 } URLProtocol;
 
 /**
@@ -138,6 +140,10 @@ int ffurl_connect(URLContext *uc, AVDictionary **options);
  * @return >= 0 in case of success, a negative value corresponding to an
  * AVERROR code in case of failure
  */
+int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
+               const AVIOInterruptCB *int_cb, AVDictionary **options,
+               const char *whitelist);
+
 int ffurl_open(URLContext **puc, const char *filename, int flags,
                const AVIOInterruptCB *int_cb, AVDictionary **options);
 
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 6bf2fd1..f8846b7 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -139,11 +139,15 @@ void av_format_inject_global_side_data(AVFormatContext *s)
 
 int ff_copy_whitelists(AVFormatContext *dst, AVFormatContext *src)
 {
-    av_assert0(!dst->codec_whitelist && !dst->format_whitelist);
+    av_assert0(!dst->codec_whitelist &&
+               !dst->format_whitelist &&
+               !dst->protocol_whitelist);
     dst-> codec_whitelist = av_strdup(src->codec_whitelist);
     dst->format_whitelist = av_strdup(src->format_whitelist);
+    dst->protocol_whitelist = av_strdup(src->protocol_whitelist);
     if (   (src-> codec_whitelist && !dst-> codec_whitelist)
-        || (src->format_whitelist && !dst->format_whitelist)) {
+        || (src->  format_whitelist && !dst->  format_whitelist)
+        || (src->protocol_whitelist && !dst->protocol_whitelist)) {
         av_log(dst, AV_LOG_ERROR, "Failed to duplicate whitelist\n");
         return AVERROR(ENOMEM);
     }
@@ -352,9 +356,11 @@ static int init_input(AVFormatContext *s, const char *filename,
         (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
         return score;
 
-    if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags,
-                          &s->interrupt_callback, options)) < 0)
+    if ((ret = ffio_open_whitelist(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags,
+                                   &s->interrupt_callback, options,
+                                   s->protocol_whitelist)) < 0)
         return ret;
+
     if (s->iformat)
         return 0;
     return av_probe_input_buffer2(s->pb, &s->iformat, filename,
@@ -441,6 +447,14 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
         goto fail;
     s->probe_score = ret;
 
+    if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
+        s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
+        if (!s->protocol_whitelist) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+    }
+
     if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
         av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
         ret = AVERROR(EINVAL);
-- 
1.7.9.5



More information about the ffmpeg-devel mailing list