From: Anton Khirnov Date: Wed, 20 Jan 2016 10:11:38 +0000 (+0100) Subject: lavf: add a protocol whitelist/blacklist for file opened internally X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=ec4c48397641dbaf4ae8df36c32aaa5a311a11bf;p=ffmpeg lavf: add a protocol whitelist/blacklist for file opened internally Should make the default behaviour safer for careless callers that open random untrusted files. Bug-Id: CVE-2016-1897 Bug-Id: CVE-2016-1898 --- diff --git a/doc/APIchanges b/doc/APIchanges index b9a00087799..b84fa4e4f50 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -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. diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 8dab2b75e79..b63541d411a 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -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 { diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 20bef660292..a2edb74974d 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -41,20 +41,53 @@ #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); diff --git a/libavformat/options.c b/libavformat/options.c index c7fa51f41fe..f0d2c47e1ef 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -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) diff --git a/libavformat/options_table.h b/libavformat/options_table.h index 8372ef36501..b566da6400a 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -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}, }; diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 1a545c78d61..f1e0780b479 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -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); } diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index c6f4d521e43..7b6a0258c81 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -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); } diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c index 9fafd6bffe2..fc85f657356 100644 --- a/libavformat/sapdec.c +++ b/libavformat/sapdec.c @@ -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; diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c index 54eb0bec387..57866440564 100644 --- a/libavformat/sapenc.c +++ b/libavformat/sapenc.c @@ -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; diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c index cfb1f8f1c93..1dbae7013d2 100644 --- a/libavformat/smoothstreamingenc.c +++ b/libavformat/smoothstreamingenc.c @@ -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; diff --git a/libavformat/version.h b/libavformat/version.h index a003a8ba2a9..990592144fc 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -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, \