This thing has been used in the past as a vector to exploit ffmpeg
library vulneraiblities, and it's also unpledgeable without some
changes.

It opens the only file it's going to write early on, so if we could
pledge immediately after that it wouldn't need wpath or cpath, but the
public methods to generate the thumbnail automatically open the file for
writing and then begin decoding the video. We can't add pledge calls
inside those methods as they're part of libffmpegthumbnailer and other
programs might call them.

The input source can use any protocol that libavformat supports (and one
that it doesn't), so depending on the input it can need inet sockets or
unix sockets, or both.


This patch adds a public method to call the private method we need, so
the image can be opened for writing and then pledge happens and then it
goes on to decode the video.

It also adds a method to determine the protocol that libav is going to
use to access the input so an appropriate pledge can be picked. This
method uses libavformat's avio_find_protocol_name() and compares it
against known protocols that were obtained with avio_enum_protocols().

A call to av_register_all() is added early on. This sets up libav's
protocol associations (which is needed for avio_find_protocol_name() to
work) and also loads the codec libraries. This also prevents later calls
to av_register_all() from trying to read in the codec libraries. This
and a fix to a bogus stat(2) call, which would happen even when remote
protocols are being used, prevents rpath being needed for non-file
protocols.

The pledges:

Local files need rpath but no sockets.

Network protocols need dns and inet. Unix sockets need unix. Neither of
those need rpath.

This means, in normal use, if it's reading a file it doesn't get sockets
and if it's accessing sockets it doesn't get to read files. The
unfortunate exceptions being some non-standard protocols that libav can
use such as concat and crypto, which can use any and multiple protocols.

Without these changes the best pledge would be:
stdio rpath wpath cpath dns inet unix


--
Carlin


Index: graphics/ffmpegthumbnailer/Makefile
===================================================================
RCS file: /cvs/ports/graphics/ffmpegthumbnailer/Makefile,v
retrieving revision 1.27
diff -u -p -u -r1.27 Makefile
--- graphics/ffmpegthumbnailer/Makefile 11 Mar 2016 19:59:14 -0000      1.27
+++ graphics/ffmpegthumbnailer/Makefile 13 Jun 2016 16:03:49 -0000
@@ -3,7 +3,7 @@
 COMMENT=       lightweight video thumbnailer for file managers
 
 DISTNAME=      ffmpegthumbnailer-2.0.8
-REVISION=      3
+REVISION=      4
 CATEGORIES=    graphics multimedia
 MASTER_SITES=  https://ffmpegthumbnailer.googlecode.com/files/
 
@@ -14,6 +14,7 @@ HOMEPAGE=     https://github.com/dirkvdb/ffm
 # GPLv2+
 PERMIT_PACKAGE_CDROM=  Yes
 
+# uses pledge()
 WANTLIB += avcodec avformat avutil c jpeg m png pthread stdc++
 WANTLIB += swscale x264
 
Index: 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_cpp
===================================================================
RCS file: 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_cpp
diff -N 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_cpp
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_cpp
  13 Jun 2016 16:03:49 -0000
@@ -0,0 +1,114 @@
+$OpenBSD$
+
+_generateThumbnail() provides a way to call the private
+generateThumbnail() method with a manually created ImageWriter, this is
+needed so pledge() can be called immediately after creating the
+ImageWriter but before the first frame of the video is decoded.
+
+_getProtocolType() provides a method to determine the pledge()
+requirements of the protocol that is being used.
+
+Avoiding the stat() call on non-file protocols helps allow for dropping
+of the rpath promise when using network protocols.
+
+--- libffmpegthumbnailer/videothumbnailer.cpp.orig     Sun Aug 26 00:07:44 2012
++++ libffmpegthumbnailer/videothumbnailer.cpp  Tue Jun 14 03:55:54 2016
+@@ -37,6 +37,10 @@
+ #include <algorithm>
+ #include <sys/stat.h>
+ 
++extern "C" {
++#include <libavformat/avformat.h>
++}
++
+ using namespace std;
+ 
+ namespace ffmpegthumbnailer
+@@ -177,6 +181,11 @@ void VideoThumbnailer::generateSmartThumbnail(MovieDec
+     videoFrame = videoFrames[bestFrame];
+ }
+ 
++void VideoThumbnailer::_generateThumbnail(const string& videoFile, 
ImageWriter& imageWriter)
++{
++    generateThumbnail(videoFile, imageWriter, NULL);
++}
++
+ void VideoThumbnailer::generateThumbnail(const string& videoFile, 
ThumbnailerImageType type, const string& outputFile, AVFormatContext* 
pAvContext)
+ {
+     ImageWriter* imageWriter = ImageWriterFactory<const 
string&>::createImageWriter(type, outputFile);
+@@ -194,7 +203,7 @@ void VideoThumbnailer::generateThumbnail(const string&
+ 
+ void VideoThumbnailer::writeImage(const string& videoFile, ImageWriter& 
imageWriter, const VideoFrame& videoFrame, int duration, vector<uint8_t*>& 
rowPointers)
+ {
+-    if (videoFile != "-")
++    if (videoFile != "-" && _getProtocolType(videoFile) == VT_PROTOCOL_FILE)
+     {
+         struct stat statInfo;
+         if (stat(videoFile.c_str(), &statInfo) == 0)
+@@ -371,5 +380,66 @@ int VideoThumbnailer::getBestThumbnailIndex(vector<Vid
+     return bestFrame;
+ }
+ 
++int VideoThumbnailer::_getProtocolType(const std::string& videoFile)
++{
++        static int res;
++        const char *protocol;
++        int i;
++
++        const char *localprotocols[] = { "data", "file", "pipe", "subfile" };
++        const char *netprotocols[] = {
++            "ffrtmphttp", "ffrtmpcrypt", "ftp", "gopher", "http",
++            "httpproxy", "https", "mmsh", "mmst", "rtmp", "rtmpe", "rtmps",
++            "rtmpt", "rtmpte", "rtmpts", "rtp", "srtp", "rtsp", "sftp",
++            "tcp", "tls", "udp", "udplite"
++        };
++
++        if (res)
++            return res;
++
++        protocol = avio_find_protocol_name(videoFile.c_str());
++
++        if (protocol == NULL)
++        {
++            /* rtsp is not treated by libavformat as a protocol so
++               avio_find_protocol_name() won't return it */
++            if (strncmp(videoFile.c_str(), "rtsp:", 5) == 0)
++            {
++                protocol = "rtsp";
++            }
++            else
++            {
++                res = VT_PROTOCOL_UNKN;
++                return res;
++            }
++        }
++
++        for (i = 0; i < sizeof(localprotocols) / sizeof(char *); i++)
++        {
++            if (strcmp(protocol, localprotocols[i]) == 0)
++            {
++                res = VT_PROTOCOL_FILE;
++                return res;
++            }
++        }
++
++        for (i = 0; i < sizeof(netprotocols) / sizeof(char *); i++)
++        {
++            if (strcmp(protocol, netprotocols[i]) == 0)
++            {
++                res = VT_PROTOCOL_INET;
++                return res;
++            }
++        }
++
++        if (strcmp(protocol, "unix") == 0)
++        {
++            res = VT_PROTOCOL_UNIX;
++            return res;
++        }
++
++        res = VT_PROTOCOL_UNKN;
++        return res;
+ }
+ 
++}
Index: 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_h
===================================================================
RCS file: 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_h
diff -N 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ 
graphics/ffmpegthumbnailer/patches/patch-libffmpegthumbnailer_videothumbnailer_h
    13 Jun 2016 16:03:49 -0000
@@ -0,0 +1,32 @@
+$OpenBSD$
+--- libffmpegthumbnailer/videothumbnailer.h.orig       Sun Aug 26 00:07:44 2012
++++ libffmpegthumbnailer/videothumbnailer.h    Tue Jun 14 01:14:14 2016
+@@ -46,6 +46,10 @@ class VideoThumbnailer (public)
+     void generateThumbnail(const std::string& videoFile, ThumbnailerImageType 
type, const std::string& outputFile, AVFormatContext* pAvContext = NULL);
+     void generateThumbnail(const std::string& videoFile, ThumbnailerImageType 
type, std::vector<uint8_t>& buffer, AVFormatContext* pAvContext = NULL);
+ 
++    /* this function is needed for pledge(2) and is not part of the
++       standard libffmpegthumbnailer interface */
++    void _generateThumbnail(const std::string& videoFile, ImageWriter& 
imageWriter);
++
+     void setThumbnailSize(int size);
+     void setSeekPercentage(int percentage);
+     void setSeekTime(const std::string& seekTime);
+@@ -56,6 +60,17 @@ class VideoThumbnailer (public)
+     void addFilter(IFilter* pFilter);
+     void removeFilter(IFilter* pFilter);
+     void clearFilters();
++
++    /* this function is needed for pledge(2) and is not part of the
++       standard libffmpegthumbnailer interface */
++    int _getProtocolType(const std::string&);
++
++    /* these defines are needed for pledge(2) and are not part of the
++       standard libffmpegthumbnailer interface */
++#define VT_PROTOCOL_UNKN 0
++#define VT_PROTOCOL_FILE 1
++#define VT_PROTOCOL_INET 2
++#define VT_PROTOCOL_UNIX 3
+ 
+ private:
+     void generateThumbnail(const std::string& videoFile, ImageWriter& 
imageWriter, AVFormatContext* pAvContext = NULL);
Index: graphics/ffmpegthumbnailer/patches/patch-main_cpp
===================================================================
RCS file: graphics/ffmpegthumbnailer/patches/patch-main_cpp
diff -N graphics/ffmpegthumbnailer/patches/patch-main_cpp
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ graphics/ffmpegthumbnailer/patches/patch-main_cpp   13 Jun 2016 16:03:49 
-0000
@@ -0,0 +1,92 @@
+$OpenBSD$
+
+pledge:
+    file - stdio rpath
+        reads a local file to produce the thumbnail
+
+    network - stdio dns inet
+        reads a remote file to produce the thumbnail
+
+    unix socket - stdio unix
+        reads data from a unix socket to produce the thumbnail
+
+In all cases cpath and wpath are avoided by calling pledge() after the
+ImageWriter has opened the file for writing.
+
+Calling av_register_all() early when inet/unix protocols are used
+avoids the need for rpath.
+
+
+--- main.cpp.orig      Sun Aug 26 00:07:44 2012
++++ main.cpp   Tue Jun 14 02:11:52 2016
+@@ -30,7 +30,12 @@
+ #include "libffmpegthumbnailer/videothumbnailer.h"
+ #include "libffmpegthumbnailer/stringoperations.h"
+ #include "libffmpegthumbnailer/filmstripfilter.h"
++#include "libffmpegthumbnailer/imagewriterfactory.h"
+ 
++extern "C" {
++#include <libavformat/avformat.h>
++}
++
+ using namespace std;
+ using namespace ffmpegthumbnailer;
+ 
+@@ -54,6 +59,7 @@ int main(int argc, char** argv)
+     string  inputFile;
+     string  outputFile;
+     string  imageFormat;
++    int     protocol;
+ 
+     while ((option = getopt (argc, argv, "i:o:s:t:q:c:afwhvp")) != -1)
+     {
+@@ -149,8 +155,48 @@ int main(int argc, char** argv)
+         {
+             videoThumbnailer.setSeekPercentage(seekPercentage);
+         }
+-        videoThumbnailer.generateThumbnail(inputFile, imageType, outputFile);
+ 
++        ImageWriter* imageWriter = ImageWriterFactory<const 
string&>::createImageWriter(imageType, outputFile);
++
++        av_register_all();
++        protocol = videoThumbnailer._getProtocolType(inputFile);
++
++        if (protocol == VT_PROTOCOL_FILE)
++        {
++            if (pledge("stdio rpath", NULL) == -1)
++            {
++                cerr << "pledge failed" << endl;
++                exit(-1);
++            }
++        }
++        else if (protocol == VT_PROTOCOL_INET)
++        {
++            if (pledge("stdio dns inet", NULL) == -1)
++            {
++                cerr << "pledge failed" << endl;
++                exit(-1);
++            }
++        }
++        else if (protocol == VT_PROTOCOL_UNIX)
++        {
++            if (pledge("stdio unix", NULL) == -1)
++            {
++                cerr << "pledge failed" << endl;
++                exit(-1);
++            }
++        }
++        else
++        {
++            if (pledge("stdio rpath dns inet unix", NULL) == -1)
++            {
++                cerr << "pledge failed" << endl;
++                exit(-1);
++            }
++        }
++
++        videoThumbnailer._generateThumbnail(inputFile, *imageWriter);
++
++        delete imageWriter;
+         delete filmStripFilter;
+     }
+     catch (exception& e)

Reply via email to