Should make the default behaviour safer for careless callers that open random untrusted files. Bug-Id: CVE-2016-1897 Bug-Id: CVE-2016-1898tags/n3.1
@@ -13,6 +13,11 @@ libavutil: 2015-08-28 | |||||
API changes, most recent first: | 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 | 2016-xx-xx - lavc 57.13.0 - avcodec.h | ||||
Add AVCodecContext.hw_frames_ctx. | Add AVCodecContext.hw_frames_ctx. | ||||
@@ -1261,6 +1261,24 @@ typedef struct AVFormatContext { | |||||
* A callback for closing the streams opened with AVFormatContext.io_open(). | * A callback for closing the streams opened with AVFormatContext.io_open(). | ||||
*/ | */ | ||||
void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); | 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; | } AVFormatContext; | ||||
typedef struct AVPacketList { | typedef struct AVPacketList { | ||||
@@ -41,20 +41,53 @@ | |||||
#define SHORT_SEEK_THRESHOLD 4096 | #define SHORT_SEEK_THRESHOLD 4096 | ||||
typedef struct AVIOInternal { | typedef struct AVIOInternal { | ||||
const AVClass *class; | |||||
char *protocol_whitelist; | |||||
char *protocol_blacklist; | |||||
URLContext *h; | URLContext *h; | ||||
const URLProtocol **protocols; | const URLProtocol **protocols; | ||||
} AVIOInternal; | } 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) | static void *ff_avio_child_next(void *obj, void *prev) | ||||
{ | { | ||||
AVIOContext *s = obj; | 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) | 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[] = { | static const AVOption ff_avio_options[] = { | ||||
@@ -750,8 +783,11 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) | |||||
if (!internal) | if (!internal) | ||||
goto fail; | goto fail; | ||||
internal->class = &io_priv_class; | |||||
internal->h = h; | internal->h = h; | ||||
av_opt_set_defaults(internal); | |||||
*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, | *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, | ||||
internal, io_read_packet, io_write_packet, io_seek); | internal, io_read_packet, io_write_packet, io_seek); | ||||
if (!*s) | if (!*s) | ||||
@@ -766,6 +802,8 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) | |||||
(*s)->av_class = &ff_avio_class; | (*s)->av_class = &ff_avio_class; | ||||
return 0; | return 0; | ||||
fail: | fail: | ||||
if (internal) | |||||
av_opt_free(internal); | |||||
av_freep(&internal); | av_freep(&internal); | ||||
av_freep(&buffer); | av_freep(&buffer); | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
@@ -849,10 +887,21 @@ int avio_open2(AVIOContext **s, const char *filename, int flags, | |||||
{ | { | ||||
AVIOInternal *internal; | AVIOInternal *internal; | ||||
const URLProtocol **protocols; | const URLProtocol **protocols; | ||||
char *proto_whitelist = NULL, *proto_blacklist = NULL; | |||||
AVDictionaryEntry *e; | |||||
URLContext *h; | URLContext *h; | ||||
int err; | 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) | if (!protocols) | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
@@ -872,6 +921,14 @@ int avio_open2(AVIOContext **s, const char *filename, int flags, | |||||
internal = (*s)->opaque; | internal = (*s)->opaque; | ||||
internal->protocols = protocols; | internal->protocols = protocols; | ||||
if (options) { | |||||
err = av_opt_set_dict(internal, options); | |||||
if (err < 0) { | |||||
avio_closep(s); | |||||
return err; | |||||
} | |||||
} | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -887,6 +944,8 @@ int avio_close(AVIOContext *s) | |||||
internal = s->opaque; | internal = s->opaque; | ||||
h = internal->h; | h = internal->h; | ||||
av_opt_free(internal); | |||||
av_freep(&internal->protocols); | av_freep(&internal->protocols); | ||||
av_freep(&s->opaque); | av_freep(&s->opaque); | ||||
av_freep(&s->buffer); | av_freep(&s->buffer); | ||||
@@ -20,6 +20,7 @@ | |||||
#include "avformat.h" | #include "avformat.h" | ||||
#include "avio_internal.h" | #include "avio_internal.h" | ||||
#include "internal.h" | #include "internal.h" | ||||
#include "url.h" | |||||
#include "libavutil/internal.h" | #include "libavutil/internal.h" | ||||
#include "libavutil/opt.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, | static int io_open_default(AVFormatContext *s, AVIOContext **pb, | ||||
const char *url, int flags, AVDictionary **options) | 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) | static void io_close_default(AVFormatContext *s, AVIOContext *pb) | ||||
@@ -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"}, | {"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_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"}, | {"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}, | {NULL}, | ||||
}; | }; | ||||
@@ -1668,7 +1668,8 @@ int ff_rtsp_connect(AVFormatContext *s) | |||||
return AVERROR(EIO); | return AVERROR(EIO); | ||||
if (!rt->protocols) { | if (!rt->protocols) { | ||||
rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
s->protocol_blacklist); | |||||
if (!rt->protocols) | if (!rt->protocols) | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
} | } | ||||
@@ -2252,7 +2253,8 @@ static int sdp_read_header(AVFormatContext *s) | |||||
return AVERROR(EIO); | return AVERROR(EIO); | ||||
if (!rt->protocols) { | if (!rt->protocols) { | ||||
rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
s->protocol_blacklist); | |||||
if (!rt->protocols) | if (!rt->protocols) | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
} | } | ||||
@@ -2379,7 +2381,8 @@ static int rtp_read_header(AVFormatContext *s) | |||||
return AVERROR(EIO); | return AVERROR(EIO); | ||||
if (!rt->protocols) { | if (!rt->protocols) { | ||||
rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
s->protocol_blacklist); | |||||
if (!rt->protocols) | if (!rt->protocols) | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
} | } | ||||
@@ -640,7 +640,8 @@ static int rtsp_listen(AVFormatContext *s) | |||||
enum RTSPMethod methodcode; | enum RTSPMethod methodcode; | ||||
if (!rt->protocols) { | if (!rt->protocols) { | ||||
rt->protocols = ffurl_get_protocols(NULL, NULL); | |||||
rt->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
s->protocol_blacklist); | |||||
if (!rt->protocols) | if (!rt->protocols) | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
} | } | ||||
@@ -85,7 +85,8 @@ static int sap_read_header(AVFormatContext *s) | |||||
av_strlcpy(host, "224.2.127.254", sizeof(host)); | 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) { | if (!sap->protocols) { | ||||
ret = AVERROR(ENOMEM); | ret = AVERROR(ENOMEM); | ||||
goto fail; | goto fail; | ||||
@@ -138,7 +138,8 @@ static int sap_write_header(AVFormatContext *s) | |||||
freeaddrinfo(ai); | freeaddrinfo(ai); | ||||
} | } | ||||
sap->protocols = ffurl_get_protocols(NULL, NULL); | |||||
sap->protocols = ffurl_get_protocols(s->protocol_whitelist, | |||||
s->protocol_blacklist); | |||||
if (!sap->protocols) { | if (!sap->protocols) { | ||||
ret = AVERROR(ENOMEM); | ret = AVERROR(ENOMEM); | ||||
goto fail; | goto fail; | ||||
@@ -312,7 +312,7 @@ static int ism_write_header(AVFormatContext *s) | |||||
goto fail; | goto fail; | ||||
} | } | ||||
c->protocols = ffurl_get_protocols(NULL, NULL); | |||||
c->protocols = ffurl_get_protocols(s->protocol_whitelist, s->protocol_blacklist); | |||||
if (!c->protocols) { | if (!c->protocols) { | ||||
ret = AVERROR(ENOMEM); | ret = AVERROR(ENOMEM); | ||||
goto fail; | goto fail; | ||||
@@ -30,7 +30,7 @@ | |||||
#include "libavutil/version.h" | #include "libavutil/version.h" | ||||
#define LIBAVFORMAT_VERSION_MAJOR 57 | #define LIBAVFORMAT_VERSION_MAJOR 57 | ||||
#define LIBAVFORMAT_VERSION_MINOR 3 | |||||
#define LIBAVFORMAT_VERSION_MINOR 4 | |||||
#define LIBAVFORMAT_VERSION_MICRO 0 | #define LIBAVFORMAT_VERSION_MICRO 0 | ||||
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | ||||