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)