From 602244979b2f4cf2c410af30460a9531a9dd7290 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 21 Sep 2017 17:47:28 +0100 Subject: [PATCH] Tar extractor can filter file paths. --- simgear/io/untar.cxx | 61 ++++++++++++++++++++++++++++++++------------ simgear/io/untar.hxx | 10 ++++++++ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/simgear/io/untar.cxx b/simgear/io/untar.cxx index 9a1dfe61..0eeab2f8 100644 --- a/simgear/io/untar.cxx +++ b/simgear/io/untar.cxx @@ -28,6 +28,7 @@ #include +#include #include #include @@ -92,7 +93,8 @@ public: END_OF_ARCHIVE, ERROR_STATE, ///< states above this are error conditions BAD_ARCHIVE, - BAD_DATA + BAD_DATA, + FILTER_STOPPED } State; SGPath path; @@ -110,10 +112,13 @@ public: bool haveInitedZLib; bool uncompressedData; // set if reading a plain .tar (not tar.gz) uint8_t* headerPtr; - - TarExtractorPrivate() : + TarExtractor* outer; + bool skipCurrentEntry = false; + + TarExtractorPrivate(TarExtractor* o) : haveInitedZLib(false), - uncompressedData(false) + uncompressedData(false), + outer(o) { } @@ -129,7 +134,10 @@ public: } if (state == READING_FILE) { - currentFile->close(); + if (currentFile) { + currentFile->close(); + currentFile.reset(); + } size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE; if (pad) { bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad; @@ -177,26 +185,36 @@ public: return; } + skipCurrentEntry = false; std::string tarPath = std::string(header.prefix) + std::string(header.fileName); if (!isSafePath(tarPath)) { - //state = BAD_ARCHIVE; SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath); - //return; + skipCurrentEntry = true; } - SGPath p = path; - p.append(tarPath); - + auto result = outer->filterPath(tarPath); + if (result == TarExtractor::Stop) { + setState(FILTER_STOPPED); + return; + } else if (result == TarExtractor::Skipped) { + skipCurrentEntry = true; + } + + SGPath p = path / tarPath; if (header.typeflag == DIRTYPE) { - Dir dir(p); - dir.create(0755); + if (!skipCurrentEntry) { + Dir dir(p); + dir.create(0755); + } setState(READING_HEADER); } else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) { currentFileSize = ::strtol(header.size, NULL, 8); bytesRemaining = currentFileSize; - currentFile.reset(new SGBinaryFile(p)); - currentFile->open(SG_IO_OUT); + if (!skipCurrentEntry) { + currentFile.reset(new SGBinaryFile(p)); + currentFile->open(SG_IO_OUT); + } setState(READING_FILE); } else { SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag); @@ -212,7 +230,9 @@ public: size_t curBytes = std::min(bytesRemaining, count); if (state == READING_FILE) { - currentFile->write(bytes, curBytes); + if (currentFile) { + currentFile->write(bytes, curBytes); + } bytesRemaining -= curBytes; } else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) { memcpy(headerPtr, bytes, curBytes); @@ -264,7 +284,7 @@ public: }; TarExtractor::TarExtractor(const SGPath& rootPath) : - d(new TarExtractorPrivate) + d(new TarExtractorPrivate(this)) { d->path = rootPath; @@ -397,7 +417,7 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count) SG_LOG(SG_IO, SG_WARN, "insufficient data for header"); return false; } - + header = reinterpret_cast(zlibOutput); } else { // uncompressed tar @@ -417,4 +437,11 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count) return true; } +auto TarExtractor::filterPath(std::string& pathToExtract) + -> PathResult +{ + SG_UNUSED(pathToExtract); + return Accepted; +} + } // of simgear diff --git a/simgear/io/untar.hxx b/simgear/io/untar.hxx index b1db9063..0d963df2 100644 --- a/simgear/io/untar.hxx +++ b/simgear/io/untar.hxx @@ -43,7 +43,17 @@ public: bool hasError() const; +protected: + enum PathResult { + Accepted, + Skipped, + Modified, + Stop + }; + + virtual PathResult filterPath(std::string& pathToExtract); private: + friend class TarExtractorPrivate; std::unique_ptr d; };