player: always load playlists

Until now, you had to use --load-unsafe-playlists or --playlist to get
playlists loaded. Change this and always load playlists by default.

This still attempts to reject unsafe URLs. For example, trying to invoke
libavdevice pseudo-demuxer is explicitly prevented. Local paths and any
http links (and some more) are always allowed.
This commit is contained in:
wm4 2014-08-31 19:49:39 +02:00
parent fc0fa9a221
commit 866e0e1670
7 changed files with 53 additions and 27 deletions

View File

@ -198,20 +198,12 @@ Playback Control
(i.e. not with stdin, pipe, etc). (i.e. not with stdin, pipe, etc).
``--load-unsafe-playlists`` ``--load-unsafe-playlists``
Normally, something like ``mpv playlist.m3u`` won't load the playlist. This Load URLs from playlists which are considered unsafe (default: no). This
is because the playlist code is unsafe. (This is the same in all other includes special protocols and anything that doesn't refer to normal files.
variations of MPlayer.) Local files and http links on the other hand are always considered safe.
See ``--playlist`` for details. Note that ``--playlist`` always loads all entries, so you use that instead
if you really have the need for this functionality.
Note: this option will allow opening playlists using the ``playlist``
special demuxer. The ``--playlist`` uses different code, and supports more
playlist formats than the playlist demuxer. This means that for now, the
``--playlist`` option should always be used if you intend to open playlists.
Background: the special demuxer contains newly written code, while the
``--playlist`` option uses the old MPlayer code. Adding support for more
playlist formats to the special demuxer is work in progress, and eventually
the old code should disappear.
``--loop-file`` ``--loop-file``
Loop a single file. The difference to ``--loop=inf`` is that this doesn't Loop a single file. The difference to ``--loop=inf`` is that this doesn't

View File

@ -41,6 +41,9 @@ struct playlist_entry {
bool playback_short : 1; bool playback_short : 1;
// Set to true if not at least 1 frame (audio or video) could be played. // Set to true if not at least 1 frame (audio or video) could be played.
bool init_failed : 1; bool init_failed : 1;
// If set, assume that this is e.g. from an external playlist, and needs an
// additional safety check.
bool unsafe_origin : 1;
}; };
struct playlist { struct playlist {

View File

@ -1084,7 +1084,10 @@ static void play_current_file(struct MPContext *mpctx)
} }
stream_filename = mpctx->resolve_result->url; stream_filename = mpctx->resolve_result->url;
} }
mpctx->stream = stream_open(stream_filename, mpctx->global); int stream_flags = STREAM_READ;
if (mpctx->playlist->current->unsafe_origin && !opts->load_unsafe_playlists)
stream_flags |= STREAM_SAFE_ONLY;
mpctx->stream = stream_create(stream_filename, stream_flags, mpctx->global);
if (!mpctx->stream) { // error... if (!mpctx->stream) { // error...
demux_was_interrupted(mpctx); demux_was_interrupted(mpctx);
goto terminate_playback; goto terminate_playback;
@ -1127,16 +1130,10 @@ goto_reopen_demuxer: ;
mpctx->initialized_flags |= INITIALIZED_DEMUXER; mpctx->initialized_flags |= INITIALIZED_DEMUXER;
if (mpctx->demuxer->playlist) { if (mpctx->demuxer->playlist) {
if (mpctx->demuxer->stream->safe_origin || opts->load_unsafe_playlists) { struct playlist *pl = mpctx->demuxer->playlist;
transfer_playlist(mpctx, mpctx->demuxer->playlist); for (struct playlist_entry *e = pl->first; e; e = e->next)
} else { e->unsafe_origin |= !mpctx->demuxer->stream->safe_origin;
MP_ERR(mpctx, "\nThis looks like a playlist, but playlist support " transfer_playlist(mpctx, pl);
"will not be used automatically.\nThe main problem with "
"playlist safety is that playlist entries can be arbitrary,\n"
"and an attacker could make mpv poke around in your local "
"filesystem or network.\nUse --playlist=file or the "
"--load-unsafe-playlists option to load them anyway.\n");
}
goto terminate_playback; goto terminate_playback;
} }

View File

@ -61,6 +61,7 @@ extern const stream_info_t stream_info_null;
extern const stream_info_t stream_info_memory; extern const stream_info_t stream_info_memory;
extern const stream_info_t stream_info_mf; extern const stream_info_t stream_info_mf;
extern const stream_info_t stream_info_ffmpeg; extern const stream_info_t stream_info_ffmpeg;
extern const stream_info_t stream_info_ffmpeg_unsafe;
extern const stream_info_t stream_info_avdevice; extern const stream_info_t stream_info_avdevice;
extern const stream_info_t stream_info_file; extern const stream_info_t stream_info_file;
extern const stream_info_t stream_info_ifo; extern const stream_info_t stream_info_ifo;
@ -77,6 +78,7 @@ static const stream_info_t *const stream_list[] = {
&stream_info_cdda, &stream_info_cdda,
#endif #endif
&stream_info_ffmpeg, &stream_info_ffmpeg,
&stream_info_ffmpeg_unsafe,
&stream_info_avdevice, &stream_info_avdevice,
#if HAVE_DVBIN #if HAVE_DVBIN
&stream_info_dvb, &stream_info_dvb,
@ -257,6 +259,8 @@ static int open_internal(const stream_info_t *sinfo, struct stream *underlying,
return STREAM_NO_MATCH; return STREAM_NO_MATCH;
if (sinfo->stream_filter && (flags & STREAM_NO_FILTERS)) if (sinfo->stream_filter && (flags & STREAM_NO_FILTERS))
return STREAM_NO_MATCH; return STREAM_NO_MATCH;
if (!sinfo->is_safe && (flags & STREAM_SAFE_ONLY))
return STREAM_UNSAFE;
const char *path = NULL; const char *path = NULL;
// Stream filters use the original URL, with no protocol matching at all. // Stream filters use the original URL, with no protocol matching at all.
@ -335,18 +339,30 @@ struct stream *stream_create(const char *url, int flags, struct mpv_global *glob
assert(url); assert(url);
// Open stream proper // Open stream proper
bool unsafe = false;
for (int i = 0; stream_list[i]; i++) { for (int i = 0; stream_list[i]; i++) {
int r = open_internal(stream_list[i], NULL, url, flags, global, &s); int r = open_internal(stream_list[i], NULL, url, flags, global, &s);
if (r == STREAM_OK) if (r == STREAM_OK)
break; break;
if (r == STREAM_NO_MATCH || r == STREAM_UNSUPPORTED) if (r == STREAM_NO_MATCH || r == STREAM_UNSUPPORTED)
continue; continue;
if (r == STREAM_UNSAFE) {
unsafe = true;
continue;
}
if (r != STREAM_OK) { if (r != STREAM_OK) {
mp_err(log, "Failed to open %s.\n", url); mp_err(log, "Failed to open %s.\n", url);
goto done; goto done;
} }
} }
if (!s && unsafe) {
mp_err(log, "\nRefusing to load potentially unsafe URL from a playlist.\n"
"Use --playlist=file or the --load-unsafe-playlists option to "
"load it anyway.\n\n");
goto done;
}
if (!s) { if (!s) {
mp_err(log, "No stream found to handle url %s\n", url); mp_err(log, "No stream found to handle url %s\n", url);
goto done; goto done;

View File

@ -55,7 +55,9 @@ enum streamtype {
// flags for stream_open_ext (this includes STREAM_READ and STREAM_WRITE) // flags for stream_open_ext (this includes STREAM_READ and STREAM_WRITE)
#define STREAM_NO_FILTERS 2 #define STREAM_NO_FILTERS 2
#define STREAM_SAFE_ONLY 4
#define STREAM_UNSAFE -3
#define STREAM_NO_MATCH -2 #define STREAM_NO_MATCH -2
#define STREAM_UNSUPPORTED -1 #define STREAM_UNSUPPORTED -1
#define STREAM_ERROR 0 #define STREAM_ERROR 0
@ -141,6 +143,7 @@ typedef struct stream_info_st {
const char *const *url_options; const char *const *url_options;
bool stream_filter; bool stream_filter;
bool can_write; bool can_write;
bool is_safe;
} stream_info_t; } stream_info_t;
typedef struct stream { typedef struct stream {

View File

@ -281,4 +281,5 @@ const stream_info_t stream_info_file = {
.open = open_f, .open = open_f,
.protocols = (const char*const[]){ "file", "", NULL }, .protocols = (const char*const[]){ "file", "", NULL },
.can_write = true, .can_write = true,
.is_safe = true,
}; };

View File

@ -322,10 +322,24 @@ const stream_info_t stream_info_ffmpeg = {
.name = "ffmpeg", .name = "ffmpeg",
.open = open_f, .open = open_f,
.protocols = (const char *const[]){ .protocols = (const char *const[]){
"lavf", "ffmpeg", "rtmp", "rtsp", "http", "https", "mms", "mmst", "mmsh", "rtmp", "rtsp", "http", "https", "mms", "mmst", "mmsh", "mmshttp", "rtp",
"mmshttp", "udp", "ftp", "rtp", "httpproxy", "hls", "rtmpe", "rtmps", "httpproxy", "hls", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srtp",
"rtmpt", "rtmpte", "rtmpts", "srtp", "tcp", "udp", "tls", "unix", "sftp",
"md5", "md5",
NULL }, NULL },
.can_write = true, .can_write = true,
.is_safe = true,
}; };
// Unlike above, this is not marked as safe, and can contain protocols which
// may do insecure things. (Such as "ffmpeg", which can access the "lavfi"
// pseudo-demuxer, which in turn gives access to filters that can access the
// local filesystem.)
const stream_info_t stream_info_ffmpeg_unsafe = {
.name = "ffmpeg",
.open = open_f,
.protocols = (const char *const[]){
"lavf", "ffmpeg", "udp", "ftp", "tcp", "tls", "unix", "sftp",
NULL },
.can_write = true,
};