summaryrefslogtreecommitdiff
path: root/libmat2/archive.py
diff options
context:
space:
mode:
authorjvoisin2019-04-28 19:25:45 +0200
committerjvoisin2019-05-01 17:55:35 +0200
commit95169906931e60ace651e8f0aefe0fbe375b2f40 (patch)
tree0a9ed871dca3104f769419bbeff91b9c2d9c56dd /libmat2/archive.py
parenta7ebb587e19ce1177a7ef067e2da74e4964ff19e (diff)
Add some verification for "dangerous" tarfiles
Diffstat (limited to 'libmat2/archive.py')
-rw-r--r--libmat2/archive.py53
1 files changed, 52 insertions, 1 deletions
diff --git a/libmat2/archive.py b/libmat2/archive.py
index 969bbd8..e6c11c1 100644
--- a/libmat2/archive.py
+++ b/libmat2/archive.py
@@ -15,7 +15,7 @@ from . import abstract, UnknownMemberPolicy, parser_factory
15assert Set 15assert Set
16assert Pattern 16assert Pattern
17 17
18# pylint: disable=not-callable,assignment-from-no-return 18# pylint: disable=not-callable,assignment-from-no-return,too-many-branches
19 19
20# An ArchiveClass is a class representing an archive, 20# An ArchiveClass is a class representing an archive,
21# while an ArchiveMember is a class representing an element 21# while an ArchiveMember is a class representing an element
@@ -238,6 +238,57 @@ class TarParser(ArchiveBasedAbstractParser):
238 def is_archive_valid(self): 238 def is_archive_valid(self):
239 if tarfile.is_tarfile(self.filename) is False: 239 if tarfile.is_tarfile(self.filename) is False:
240 raise ValueError 240 raise ValueError
241 self.__check_tarfile_safety()
242
243 def __check_tarfile_safety(self):
244 """Checks if the tarfile doesn't have any "suspicious" members.
245
246 This is a rewrite of this patch: https://bugs.python.org/file47826/safetarfile-4.diff
247 inspired by this bug from 2014: https://bugs.python.org/issue21109
248 because Python's stdlib doesn't provide a way to "safely" extract
249 things from a tar file.
250 """
251 names = set()
252 with tarfile.open(self.filename) as f:
253 members = f.getmembers()
254 for member in members:
255 name = member.name
256 if os.path.isabs(name):
257 raise ValueError("The archive %s contains a file with an " \
258 "absolute path: %s" % (self.filename, name))
259 elif os.path.normpath(name).startswith('../') or '/../' in name:
260 raise ValueError("The archive %s contains a file with an " \
261 "path traversal attack: %s" % (self.filename, name))
262
263 if name in names:
264 raise ValueError("The archive %s contains two times the same " \
265 "file: %s" % (self.filename, name))
266 else:
267 names.add(name)
268
269 if member.isfile():
270 if member.mode & stat.S_ISUID:
271 raise ValueError("The archive %s contains a setuid file: %s" % \
272 (self.filename, name))
273 elif member.mode & stat.S_ISGID:
274 raise ValueError("The archive %s contains a setgid file: %s" % \
275 (self.filename, name))
276 elif member.issym():
277 linkname = member.linkname
278 if os.path.normpath(linkname).startswith('..'):
279 raise ValueError('The archive %s contains a symlink pointing' \
280 'outside of the archive via a path traversal: %s -> %s' % \
281 (self.filename, name, linkname))
282 if os.path.isabs(linkname):
283 raise ValueError('The archive %s contains a symlink pointing' \
284 'outside of the archive: %s -> %s' % \
285 (self.filename, name, linkname))
286 elif member.isdev():
287 raise ValueError("The archive %s contains a non-regular " \
288 "file: %s" % (self.filename, name))
289 elif member.islnk():
290 raise ValueError("The archive %s contains a hardlink: %s" \
291 % (self.filename, name))
241 292
242 @staticmethod 293 @staticmethod
243 def _clean_member(member: ArchiveMember) -> ArchiveMember: 294 def _clean_member(member: ArchiveMember) -> ArchiveMember: