Index: utils/ofzip/OFZIP.h ================================================================== --- utils/ofzip/OFZIP.h +++ utils/ofzip/OFZIP.h @@ -18,10 +18,12 @@ #import "OFObject.h" #import "OFString.h" #import "Archive.h" +OF_ASSUME_NONNULL_BEGIN + #ifndef S_IRWXG # define S_IRWXG 0 #endif #ifndef S_IRWXO # define S_IRWXO 0 @@ -42,6 +44,9 @@ - (bool)shouldExtractFile: (OFString *)fileName outFileName: (OFString *)outFileName; - (ssize_t)copyBlockFromStream: (OFStream *)input toStream: (OFStream *)output fileName: (OFString *)fileName; +- (nullable OFString *)safeLocalPathForPath: (OFString *)path; @end + +OF_ASSUME_NONNULL_END Index: utils/ofzip/OFZIP.m ================================================================== --- utils/ofzip/OFZIP.m +++ utils/ofzip/OFZIP.m @@ -575,6 +575,52 @@ return -1; } return length; } + +- (OFString *)safeLocalPathForPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + + path = [path stringByStandardizingPath]; + +#if defined(OF_WINDOWS) || defined(OF_MSDOS) + if ([path containsString: @":"] || [path hasPrefix: @"\\"]) { +#elif defined(OF_AMIGAOS) + if ([path containsString: @":"] || [path hasPrefix: @"/"]) { +#else + if ([path hasPrefix: @"/"]) { +#endif + objc_autoreleasePoolPop(pool); + return nil; + } + + if ([path length] == 0) { + objc_autoreleasePoolPop(pool); + return nil; + } + + /* + * After -[stringByStandardizingPath], everything representing parent + * directory should be at the beginning, so in theory checking the + * first component should be enough. But it does not hurt being + * paranoid and checking all components, just in case. + */ + for (OFString *component in [path pathComponents]) { +#ifdef OF_AMIGAOS + if ([component length] == 0 || [component isEqual: @"/"]) { +#else + if ([component length] == 0 || [component isEqual: @".."]) { +#endif + objc_autoreleasePoolPop(pool); + return nil; + } + } + + [path retain]; + + objc_autoreleasePoolPop(pool); + + return [path autorelease]; +} @end Index: utils/ofzip/TarArchive.m ================================================================== --- utils/ofzip/TarArchive.m +++ utils/ofzip/TarArchive.m @@ -253,13 +253,11 @@ while ((entry = [_archive nextEntry]) != nil) { void *pool = objc_autoreleasePoolPush(); OFString *fileName = [entry fileName]; of_tar_archive_entry_type_t type = [entry type]; - OFString *outFileName = [fileName stringByStandardizingPath]; - OFArray OF_GENERIC(OFString *) *pathComponents; - OFString *directory; + OFString *outFileName, *directory; OFFile *output; OFStream *stream; uint64_t written = 0, size = [entry size]; int8_t percent = -1, newPercent; @@ -276,40 +274,21 @@ continue; } [missing removeObject: fileName]; -#if !defined(OF_WINDOWS) && !defined(OF_MSDOS) - if ([outFileName hasPrefix: @"/"]) { -#else - if ([outFileName hasPrefix: @"/"] || - [outFileName containsString: @":"]) { -#endif + outFileName = [app safeLocalPathForPath: fileName]; + if (outFileName == nil) { [of_stderr writeLine: OF_LOCALIZED( @"refusing_to_extract_file", @"Refusing to extract %[file]!", @"file", fileName)]; app->_exitStatus = 1; goto outer_loop_end; } - pathComponents = [outFileName pathComponents]; - for (OFString *component in pathComponents) { - if ([component length] == 0 || - [component isEqual: @".."]) { - [of_stderr writeLine: OF_LOCALIZED( - @"refusing_to_extract_file", - @"Refusing to extract %[file]!", - @"file", fileName)]; - - app->_exitStatus = 1; - goto outer_loop_end; - } - } - outFileName = [OFString pathWithComponents: pathComponents]; - if (app->_outputLevel >= 0) [of_stdout writeString: OF_LOCALIZED(@"extracting_file", @"Extracting %[file]...", @"file", fileName)]; Index: utils/ofzip/ZIPArchive.m ================================================================== --- utils/ofzip/ZIPArchive.m +++ utils/ofzip/ZIPArchive.m @@ -205,13 +205,11 @@ [OFMutableSet setWithArray: files]; for (OFZIPArchiveEntry *entry in [_archive entries]) { void *pool = objc_autoreleasePoolPush(); OFString *fileName = [entry fileName]; - OFString *outFileName = [fileName stringByStandardizingPath]; - OFArray OF_GENERIC(OFString *) *pathComponents; - OFString *directory; + OFString *outFileName, *directory; OFStream *stream; OFFile *output; uint64_t written = 0, size = [entry uncompressedSize]; int8_t percent = -1, newPercent; @@ -218,40 +216,21 @@ if (!all && ![files containsObject: fileName]) continue; [missing removeObject: fileName]; -#if !defined(OF_WINDOWS) && !defined(OF_MSDOS) - if ([outFileName hasPrefix: @"/"]) { -#else - if ([outFileName hasPrefix: @"/"] || - [outFileName containsString: @":"]) { -#endif + outFileName = [app safeLocalPathForPath: fileName]; + if (outFileName == nil) { [of_stderr writeLine: OF_LOCALIZED( @"refusing_to_extract_file", @"Refusing to extract %[file]!", @"file", fileName)]; app->_exitStatus = 1; goto outer_loop_end; } - pathComponents = [outFileName pathComponents]; - for (OFString *component in pathComponents) { - if ([component length] == 0 || - [component isEqual: @".."]) { - [of_stderr writeLine: OF_LOCALIZED( - @"refusing_to_extract_file", - @"Refusing to extract %[file]!", - @"file", fileName)]; - - app->_exitStatus = 1; - goto outer_loop_end; - } - } - outFileName = [OFString pathWithComponents: pathComponents]; - if (app->_outputLevel >= 0) [of_stdout writeString: OF_LOCALIZED(@"extracting_file", @"Extracting %[file]...", @"file", fileName)];