]> git.sesse.net Git - ffmpeg/commitdiff
lavf: add a protocol whitelist/blacklist for file opened internally
authorAnton Khirnov <anton@khirnov.net>
Wed, 20 Jan 2016 10:11:38 +0000 (11:11 +0100)
committerAnton Khirnov <anton@khirnov.net>
Mon, 22 Feb 2016 10:48:30 +0000 (11:48 +0100)
Should make the default behaviour safer for careless callers that open
random untrusted files.

Bug-Id: CVE-2016-1897
Bug-Id: CVE-2016-1898

doc/APIchanges
libavformat/avformat.h
libavformat/aviobuf.c
libavformat/options.c
libavformat/options_table.h
libavformat/rtsp.c
libavformat/rtspdec.c
libavformat/sapdec.c
libavformat/sapenc.c
libavformat/smoothstreamingenc.c
libavformat/version.h

index b9a00087799e30086ab0ce1df47f1680b303d192..b84fa4e4f501543b41f380fdff65091ccb0cc22c 100644 (file)
@@ -13,6 +13,11 @@ libavutil:     2015-08-28
 
 API changes, most recent first:
 
+2016-xx-xx - xxxxxxx - lavf 57.4.0 - avformat.h
+  Add AVFormatContext.protocol_whitelist and protocol_blacklist.
+  Add 'protocol_whitelist' and 'protocol_blacklist' private options for
+  avio_open2().
+
 2016-xx-xx - lavc 57.13.0 - avcodec.h
   Add AVCodecContext.hw_frames_ctx.
 
index 8dab2b75e790b5a706cb140037fdc3f0be99349e..b63541d411a7296ba4e240808794a8bc725424fb 100644 (file)
@@ -1261,6 +1261,24 @@ typedef struct AVFormatContext {
      * A callback for closing the streams opened with AVFormatContext.io_open().
      */
     void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
+
+    /**
+     * A comma-separated list of protocol names that will not be used internally
+     * by libavformat. If this field is a non-empty string, then protocols
+     * listed here will be forbidden.
+     *
+     * This field should be set using AVOptions.
+     */
+    char *protocol_blacklist;
+
+    /**
+     * A comma-separated list of protocol names that can be used internally by
+     * libavformat. If this field is a non-empty string, all protocols not
+     * listed here will be forbidden.
+     *
+     * This field should be set using AVOptions.
+     */
+    char *protocol_whitelist;
 } AVFormatContext;
 
 typedef struct AVPacketList {
index 20bef66029240a0a6381ba44cca6665d618a7a0f..a2edb74974de65178e1fa09ca4884c2e198e20b5 100644 (file)
 #define SHORT_SEEK_THRESHOLD 4096
 
 typedef struct AVIOInternal {
+    const AVClass *class;
+
+    char *protocol_whitelist;
+    char *protocol_blacklist;
+
     URLContext *h;
     const URLProtocol **protocols;
 } AVIOInternal;
 
+static void *io_priv_child_next(void *obj, void *prev)
+{
+    AVIOInternal *internal = obj;
+    return prev ? NULL : internal->h;
+}
+
+static const AVClass *io_priv_child_class_next(const AVClass *prev)
+{
+    return prev ? NULL : &ffurl_context_class;
+}
+
+#define OFFSET(x) offsetof(AVIOInternal, x)
+static const AVOption io_priv_options[] = {
+    { "protocol_whitelist", "A comma-separated list of allowed protocols",
+        OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING },
+    { "protocol_blacklist", "A comma-separated list of forbidden protocols",
+        OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING },
+    { NULL },
+};
+
+static const AVClass io_priv_class = {
+    .class_name       = "AVIOContext",
+    .item_name        = av_default_item_name,
+    .version          = LIBAVUTIL_VERSION_INT,
+    .option           = io_priv_options,
+    .child_next       = io_priv_child_next,
+    .child_class_next = io_priv_child_class_next,
+};
+
 static void *ff_avio_child_next(void *obj, void *prev)
 {
     AVIOContext *s = obj;
-    AVIOInternal *internal = s->opaque;
-    return prev ? NULL : internal->h;
+    return prev ? NULL : s->opaque;
 }
 
 static const AVClass *ff_avio_child_class_next(const AVClass *prev)
 {
-    return prev ? NULL : &ffurl_context_class;
+    return prev ? NULL : &io_priv_class;
 }
 
 static const AVOption ff_avio_options[] = {
@@ -750,8 +783,11 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
     if (!internal)
         goto fail;
 
+    internal->class = &io_priv_class;
     internal->h = h;
 
+    av_opt_set_defaults(internal);
+
     *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE,
                             internal, io_read_packet, io_write_packet, io_seek);
     if (!*s)
@@ -766,6 +802,8 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
     (*s)->av_class = &ff_avio_class;
     return 0;
 fail:
+    if (internal)
+        av_opt_free(internal);
     av_freep(&internal);
     av_freep(&buffer);
     return AVERROR(ENOMEM);
@@ -849,10 +887,21 @@ int avio_open2(AVIOContext **s, const char *filename, int flags,
 {
     AVIOInternal *internal;
     const URLProtocol **protocols;
+    char *proto_whitelist = NULL, *proto_blacklist = NULL;
+    AVDictionaryEntry *e;
     URLContext *h;
     int err;
 
-    protocols = ffurl_get_protocols(NULL, NULL);
+    if (options) {
+        e = av_dict_get(*options, "protocol_whitelist", NULL, 0);
+        if (e)
+            proto_whitelist = e->value;
+        e = av_dict_get(*options, "protocol_blacklist", NULL, 0);
+        if (e)
+            proto_blacklist = e->value;
+    }
+
+    protocols = ffurl_get_protocols(proto_whitelist, proto_blacklist);
     if (!protocols)
         return AVERROR(ENOMEM);
 
@@ -872,6 +921,14 @@ int avio_open2(AVIOContext **s, const char *filename, int flags,
     internal = (*s)->opaque;
     internal->protocols = protocols;
 
+    if (options) {
+        err = av_opt_set_dict(internal, options);
+        if (err < 0) {
+            avio_closep(s);
+            return err;
+        }
+    }
+
     return 0;
 }
 
@@ -887,6 +944,8 @@ int avio_close(AVIOContext *s)
     internal = s->opaque;
     h        = internal->h;
 
+    av_opt_free(internal);
+
     av_freep(&internal->protocols);
     av_freep(&s->opaque);
     av_freep(&s->buffer);
index c7fa51f41fe30b789598acba991d3a8e3b8f2fce..f0d2c47e1ef88ccad2a3832d43b5f1c44567d845 100644 (file)
@@ -20,6 +20,7 @@
 #include "avformat.h"
 #include "avio_internal.h"
 #include "internal.h"
+#include "url.h"
 
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
@@ -93,7 +94,26 @@ static const AVClass av_format_context_class = {
 static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                            const char *url, int flags, AVDictionary **options)
 {
-    return avio_open2(pb, url, flags, &s->interrupt_callback, options);
+    AVDictionary *opts_local = NULL;
+    int ret;
+
+    if (!options)
+        options = &opts_local;
+
+    if (s->protocol_whitelist) {
+        ret = av_dict_set(options, "protocol_whitelist", s->protocol_whitelist, 0);
+        if (ret < 0)
+            goto finish;
+    }
+    if (s->protocol_blacklist) {
+        ret = av_dict_set(options, "protocol_blacklist", s->protocol_blacklist, 0);
+        if (ret < 0)
+            goto finish;
+    }
+    ret = avio_open2(pb, url, flags, &s->interrupt_callback, options);
+finish:
+    av_dict_free(&opts_local);
+    return ret;
 }
 
 static void io_close_default(AVFormatContext *s, AVIOContext *pb)
index 8372ef36501bf71d9bee79c38d35717aaa8c6608..b566da6400a55f2fefe24a2319668ffb78ecc139 100644 (file)
@@ -70,6 +70,10 @@ static const AVOption avformat_options[] = {
 {"auto",              "enabled when required by target format",    0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO },              INT_MIN, INT_MAX, E, "avoid_negative_ts"},
 {"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
 {"make_zero",         "shift timestamps so they start at 0",       0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO },         INT_MIN, INT_MAX, E, "avoid_negative_ts"},
+{"protocol_blacklist", "A comma-separated list of blacklisted protocols used for opening files internally by lavf",
+                       OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = "concat" }, .flags = E | D },
+{"protocol_whitelist", "A comma-separated list of whitelisted protocols used for opening files internally by lavf",
+                       OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = E | D },
 {NULL},
 };
 
index 1a545c78d61484b2d3f67b5c3af144dfaf71fd14..f1e0780b479eb6ad87176dd26183e6f6e3e9608a 100644 (file)
@@ -1668,7 +1668,8 @@ int ff_rtsp_connect(AVFormatContext *s)
         return AVERROR(EIO);
 
     if (!rt->protocols) {
-        rt->protocols = ffurl_get_protocols(NULL, NULL);
+        rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
+                                            s->protocol_blacklist);
         if (!rt->protocols)
             return AVERROR(ENOMEM);
     }
@@ -2252,7 +2253,8 @@ static int sdp_read_header(AVFormatContext *s)
         return AVERROR(EIO);
 
     if (!rt->protocols) {
-        rt->protocols = ffurl_get_protocols(NULL, NULL);
+        rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
+                                            s->protocol_blacklist);
         if (!rt->protocols)
             return AVERROR(ENOMEM);
     }
@@ -2379,7 +2381,8 @@ static int rtp_read_header(AVFormatContext *s)
         return AVERROR(EIO);
 
     if (!rt->protocols) {
-        rt->protocols = ffurl_get_protocols(NULL, NULL);
+        rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
+                                            s->protocol_blacklist);
         if (!rt->protocols)
             return AVERROR(ENOMEM);
     }
index c6f4d521e43c427fbe97d5bb84751cdf007a4b1d..7b6a0258c8194bfefa8d4abfebc1b6d02155c78d 100644 (file)
@@ -640,7 +640,8 @@ static int rtsp_listen(AVFormatContext *s)
     enum RTSPMethod methodcode;
 
     if (!rt->protocols) {
-        rt->protocols = ffurl_get_protocols(NULL, NULL);
+        rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
+                                            s->protocol_blacklist);
         if (!rt->protocols)
             return AVERROR(ENOMEM);
     }
index 9fafd6bffe2eb86114296dd99a8a8ea063cb664d..fc85f6573565cf50adcfa3a7677557968b0436a1 100644 (file)
@@ -85,7 +85,8 @@ static int sap_read_header(AVFormatContext *s)
         av_strlcpy(host, "224.2.127.254", sizeof(host));
     }
 
-    sap->protocols = ffurl_get_protocols(NULL, NULL);
+    sap->protocols = ffurl_get_protocols(s->protocol_whitelist,
+                                         s->protocol_blacklist);
     if (!sap->protocols) {
         ret = AVERROR(ENOMEM);
         goto fail;
index 54eb0bec387fa215aa9ec0517b165043635f89f6..57866440564d05fa1b0b3fe97d592ab4bce3320d 100644 (file)
@@ -138,7 +138,8 @@ static int sap_write_header(AVFormatContext *s)
         freeaddrinfo(ai);
     }
 
-    sap->protocols = ffurl_get_protocols(NULL, NULL);
+    sap->protocols = ffurl_get_protocols(s->protocol_whitelist,
+                                         s->protocol_blacklist);
     if (!sap->protocols) {
         ret = AVERROR(ENOMEM);
         goto fail;
index cfb1f8f1c9304d2caaacab08fd3b8d7a4544f87a..1dbae7013d257e72955327a248f14f2fdcc4d8ce 100644 (file)
@@ -312,7 +312,7 @@ static int ism_write_header(AVFormatContext *s)
         goto fail;
     }
 
-    c->protocols = ffurl_get_protocols(NULL, NULL);
+    c->protocols = ffurl_get_protocols(s->protocol_whitelist, s->protocol_blacklist);
     if (!c->protocols) {
         ret = AVERROR(ENOMEM);
         goto fail;
index a003a8ba2a99efca7c8c0e743926b3791d98a843..990592144fc2c2a63593487e04c804f3fdc9b4ee 100644 (file)
@@ -30,7 +30,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 57
-#define LIBAVFORMAT_VERSION_MINOR  3
+#define LIBAVFORMAT_VERSION_MINOR  4
 #define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \