diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 7701863e9a..2a6f7790fc 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -60,6 +60,26 @@ backslash or single quotes. @end table +@subsection Options + +This demuxer accepts the following option: + +@table @option + +@item safe +If set to 1, reject unsafe file paths. A file path is considered safe if it +does not contain a protocol specification and is relative and all components +only contain characters from the portable character set (letters, digits, +period, underscore and hyphen) and have no period at the beginning of a +component. + +If set to 0, any file name is accepted. + +The default is -1, it is equivalent to 1 if the format was automatically +probed and 0 otherwise. + +@end table + @section image2 Image file demuxer. diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c index 0514755e0a..bba99d8e66 100644 --- a/libavformat/concatdec.c +++ b/libavformat/concatdec.c @@ -19,6 +19,7 @@ */ #include "libavutil/avstring.h" +#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" @@ -29,10 +30,12 @@ typedef struct { } ConcatFile; typedef struct { + AVClass *class; ConcatFile *files; ConcatFile *cur_file; unsigned nb_files; AVFormatContext *avf; + int safe; } ConcatContext; static int concat_probe(AVProbeData *probe) @@ -51,6 +54,25 @@ static char *get_keyword(uint8_t **cursor) return ret; } +static int safe_filename(const char *f) +{ + const char *start = f; + + for (; *f; f++) { + /* A-Za-z0-9_- */ + if (!((unsigned)((*f | 32) - 'a') < 26 || + (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) { + if (f == start) + return 0; + else if (*f == '/') + start = f + 1; + else if (*f != '.') + return 0; + } + } + return 1; +} + #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0) static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile, @@ -61,6 +83,10 @@ static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile, char *url; size_t url_len; + if (cat->safe > 0 && !safe_filename(filename)) { + av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename); + return AVERROR(EPERM); + } url_len = strlen(avf->filename) + strlen(filename) + 16; if (!(url = av_malloc(url_len))) return AVERROR(ENOMEM); @@ -212,6 +238,23 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) return ret; } +#define OFFSET(x) offsetof(ConcatContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "safe", "enable safe mode", + OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC }, + { NULL } +}; + +static const AVClass concat_class = { + .class_name = "concat demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + AVInputFormat ff_concat_demuxer = { .name = "concat", .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"), @@ -220,4 +263,5 @@ AVInputFormat ff_concat_demuxer = { .read_header = concat_read_header, .read_packet = concat_read_packet, .read_close = concat_read_close, + .priv_class = &concat_class, };