From: Pawel Wasowski
Date: Wed, 8 Jan 2020 10:28:08 +0000 (+0100)
Subject: [Archive] Prevent extracting files with ".." in relative paths
X-Git-Tag: submit/tizen/20200116.071506^2^2^2^2~1
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F66%2F221966%2F1;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git
[Archive] Prevent extracting files with ".." in relative paths
This change mitigates a potential security issue, which could occur if
a zip archive contained files with ".." in their paths.
ArchiveEntry.extract() and Archive.extractAll() will not extract such
files.
Verification: auto tct-tizen-archive-tests pass rate: 100%
Manual tests: attempts to extract files with forbidden
".." results in an UnknownError.
Change-Id: I563744d834d24e896493f55d15e579e714d539f9
Signed-off-by: Pawel Wasowski
---
diff --git a/src/archive/archive_file.cc b/src/archive/archive_file.cc
index 797f9632..39da1ec2 100644
--- a/src/archive/archive_file.cc
+++ b/src/archive/archive_file.cc
@@ -256,6 +256,30 @@ PlatformResult ArchiveFile::addOperation(OperationCallbackData* callback) {
return PlatformResult(ErrorCode::NO_ERROR);
}
+namespace {
+
+PlatformResult anyFilePathContainsForbiddenSubstrings(UnZip& unzip) {
+ unsigned long irrelevant_info = 0;
+ ArchiveFileEntryPtrMapPtr entries_map = std::make_shared();
+ auto result = unzip.listEntries(&irrelevant_info, &entries_map);
+ if (!result) {
+ LoggerE("Failed to list archive entries");
+ return result;
+ }
+
+ for (auto& file_path_and_file_info : *entries_map) {
+ const auto& file_path = file_path_and_file_info.first;
+ if (pathContainsProhibitedSubstrings(file_path)) {
+ const std::string error_msg = "File path " + file_path + " conatins a forbidden substring";
+ return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg, ("%s", error_msg.c_str()));
+ }
+ }
+
+ return PlatformResult{ErrorCode::NO_ERROR};
+}
+
+} // namespace
+
PlatformResult ArchiveFile::extractAllTask(ExtractAllProgressCallback* callback) {
ScopeLogger();
filesystem::FilePtr directory = callback->getDirectory();
@@ -305,6 +329,11 @@ PlatformResult ArchiveFile::extractAllTask(ExtractAllProgressCallback* callback)
return result;
}
+ result = anyFilePathContainsForbiddenSubstrings(*unzip);
+ if (!result) {
+ return result;
+ }
+
return unzip->extractAllFilesTo(directory->getNode()->getPath()->getFullPath(), callback);
}
diff --git a/src/archive/archive_instance.cc b/src/archive/archive_instance.cc
index 83063e75..092ab22b 100644
--- a/src/archive/archive_instance.cc
+++ b/src/archive/archive_instance.cc
@@ -529,6 +529,14 @@ void ArchiveInstance::Extract(const picojson::value& args, picojson::object& out
picojson::value v_entry_name = data.at(PARAM_NAME);
const double callbackId = args.get(JSON_CALLBACK_ID).get();
+ const auto entry_path = v_entry_name.get();
+ if (pathContainsProhibitedSubstrings(entry_path)) {
+ const std::string error_msg = "Entry path " + entry_path + " contains a prohibited substring";
+ LoggerE("%s", error_msg.c_str());
+ PostError(PlatformResult{ErrorCode::UNKNOWN_ERR, error_msg}, callbackId);
+ return;
+ }
+
const long operationId = static_cast(v_op_id.get());
const long handle = static_cast(v_handle.get());
diff --git a/src/archive/archive_utils.cc b/src/archive/archive_utils.cc
index 3c91f2ba..f07be1de 100644
--- a/src/archive/archive_utils.cc
+++ b/src/archive/archive_utils.cc
@@ -224,5 +224,17 @@ std::string getArchiveLogMessage(const int errorCode, const std::string& hint) {
return std::string(ss.str());
}
+bool pathContainsProhibitedSubstrings(const std::string& path) {
+ ScopeLogger("Path: %s", path.c_str());
+
+ for (const auto& prohibited_substring : kProhibitedSubstrings) {
+ if (path.find(prohibited_substring) != std::string::npos) {
+ LoggerE("Prohibited substring found: %s", prohibited_substring.c_str());
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace archive
} // namespace extension
diff --git a/src/archive/archive_utils.h b/src/archive/archive_utils.h
index 74e7aa5d..e3a0feb7 100644
--- a/src/archive/archive_utils.h
+++ b/src/archive/archive_utils.h
@@ -105,6 +105,14 @@ std::string getBasePathFromPath(const std::string& fullpath);
std::string getArchiveLogMessage(const int errorCode, const std::string& hint);
+/**
+ * The API will not allow some operations on files that contain forbidden
+ * substrings in their paths. This is done to prevent path traversal attacks,
+ * i.e. extracting files to paths that the application should not access.
+ */
+const std::vector kProhibitedSubstrings = {".."};
+bool pathContainsProhibitedSubstrings(const std::string& path);
+
} // namespace archive
} // namespace extension
diff --git a/src/archive/un_zip_extract_request.cc b/src/archive/un_zip_extract_request.cc
index 69964704..4772c5c2 100644
--- a/src/archive/un_zip_extract_request.cc
+++ b/src/archive/un_zip_extract_request.cc
@@ -94,8 +94,9 @@ void createMissingDirectories(const std::string& path, bool check_first = true)
// permissions would be changed after extract process would be finished,
// for now using default 0775
if (mkdir(left_part.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) {
- LoggerE("Couldn't create new directory: %s errno: %s", left_part.c_str(),
- GetErrorString(errno).c_str());
+ int errno_copy = errno;
+ LoggerE("Couldn't create new directory: %s errno: %d (%s)", left_part.c_str(), errno,
+ GetErrorString(errno_copy).c_str());
}
}
}