Merge pull request #296 from fritzt0/master
Added password validation & prevent stale file entries in case of invalid password
This commit is contained in:
@@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// Password check
|
||||
+ (BOOL)isFilePasswordProtectedAtPath:(NSString *)path;
|
||||
+ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError * __nullable * __nullable)error NS_SWIFT_NOTHROW;
|
||||
|
||||
// Unzip
|
||||
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination;
|
||||
|
||||
+130
-58
@@ -59,6 +59,75 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError **)error {
|
||||
if (error) {
|
||||
*error = nil;
|
||||
}
|
||||
|
||||
zipFile zip = unzOpen((const char*)[path UTF8String]);
|
||||
if (zip == NULL) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain"
|
||||
code:-1
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"failed to open zip file"}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
int ret = unzGoToFirstFile(zip);
|
||||
if (ret == UNZ_OK) {
|
||||
do {
|
||||
if ([pw length] == 0) {
|
||||
ret = unzOpenCurrentFile(zip);
|
||||
} else {
|
||||
ret = unzOpenCurrentFilePassword(zip, [pw cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
}
|
||||
if (ret != UNZ_OK) {
|
||||
if (ret != UNZ_BADPASSWORD) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain"
|
||||
code:-2
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"failed to open first file in zip file"}];
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
unz_file_info fileInfo = {0};
|
||||
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (ret != UNZ_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain"
|
||||
code:-3
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
|
||||
}
|
||||
return NO;
|
||||
} else if((fileInfo.flag & 1) == 1) {
|
||||
unsigned char buffer[10] = {0};
|
||||
int readBytes = unzReadCurrentFile(zip, buffer, (unsigned)MIN(10UL,fileInfo.uncompressed_size));
|
||||
if (readBytes < 0) {
|
||||
// Let's assume the invalid password caused this error
|
||||
if (readBytes != Z_DATA_ERROR) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain"
|
||||
code:-4
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"failed to read contents of file entry"}];
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
unzCloseCurrentFile(zip);
|
||||
ret = unzGoToNextFile(zip);
|
||||
} while (ret==UNZ_OK && UNZ_OK!=UNZ_END_OF_LIST_OF_FILE);
|
||||
|
||||
}
|
||||
|
||||
// No password required
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Unzipping
|
||||
|
||||
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination
|
||||
@@ -304,77 +373,80 @@
|
||||
}
|
||||
|
||||
if (!fileIsSymbolicLink) {
|
||||
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
|
||||
while (fp) {
|
||||
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
|
||||
|
||||
if (readBytes > 0) {
|
||||
fwrite(buffer, readBytes, 1, fp );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
if ([[[fullPath pathExtension] lowercaseString] isEqualToString:@"zip"]) {
|
||||
NSLog(@"Unzipping nested .zip file: %@", [fullPath lastPathComponent]);
|
||||
if ([self unzipFileAtPath:fullPath toDestination:[fullPath stringByDeletingLastPathComponent] overwrite:overwrite password:password error:nil delegate:nil ]) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil];
|
||||
// ensure we are not creating stale file entries
|
||||
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
|
||||
if (readBytes >= 0) {
|
||||
FILE *fp = fopen((const char*)[fullPath UTF8String], "wb");
|
||||
while (fp) {
|
||||
if (readBytes > 0) {
|
||||
fwrite(buffer, readBytes, 1, fp );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
readBytes = unzReadCurrentFile(zip, buffer, 4096);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (preserveAttributes) {
|
||||
|
||||
// Set the original datetime property
|
||||
if (fileInfo.dosDate != 0) {
|
||||
NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate];
|
||||
NSDictionary *attr = @{NSFileModificationDate: orgDate};
|
||||
|
||||
if (attr) {
|
||||
if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) {
|
||||
// Can't set attributes
|
||||
NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date");
|
||||
if (fp) {
|
||||
if ([[[fullPath pathExtension] lowercaseString] isEqualToString:@"zip"]) {
|
||||
NSLog(@"Unzipping nested .zip file: %@", [fullPath lastPathComponent]);
|
||||
if ([self unzipFileAtPath:fullPath toDestination:[fullPath stringByDeletingLastPathComponent] overwrite:overwrite password:password error:nil delegate:nil ]) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (preserveAttributes) {
|
||||
|
||||
// Set the original datetime property
|
||||
if (fileInfo.dosDate != 0) {
|
||||
NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate];
|
||||
NSDictionary *attr = @{NSFileModificationDate: orgDate};
|
||||
|
||||
if (attr) {
|
||||
if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) {
|
||||
// Can't set attributes
|
||||
NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the original permissions on the file
|
||||
uLong permissions = fileInfo.external_fa >> 16;
|
||||
if (permissions != 0) {
|
||||
// Store it into a NSNumber
|
||||
NSNumber *permissionsValue = @(permissions);
|
||||
|
||||
// Retrieve any existing attributes
|
||||
NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]];
|
||||
// Set the original permissions on the file
|
||||
uLong permissions = fileInfo.external_fa >> 16;
|
||||
if (permissions != 0) {
|
||||
// Store it into a NSNumber
|
||||
NSNumber *permissionsValue = @(permissions);
|
||||
|
||||
// Set the value in the attributes dict
|
||||
attrs[NSFilePosixPermissions] = permissionsValue;
|
||||
// Retrieve any existing attributes
|
||||
NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]];
|
||||
|
||||
// Update attributes
|
||||
if ([fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil] == NO) {
|
||||
// Unable to set the permissions attribute
|
||||
NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions");
|
||||
}
|
||||
// Set the value in the attributes dict
|
||||
attrs[NSFilePosixPermissions] = permissionsValue;
|
||||
|
||||
// Update attributes
|
||||
if ([fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil] == NO) {
|
||||
// Unable to set the permissions attribute
|
||||
NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions");
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
[attrs release];
|
||||
[attrs release];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we couldn't open file descriptor we can validate global errno to see the reason
|
||||
if (errno == ENOSPC) {
|
||||
NSError *enospcError = [NSError errorWithDomain:NSPOSIXErrorDomain
|
||||
code:ENOSPC
|
||||
userInfo:nil];
|
||||
unzippingError = enospcError;
|
||||
unzCloseCurrentFile(zip);
|
||||
success = NO;
|
||||
break;
|
||||
else
|
||||
{
|
||||
// if we couldn't open file descriptor we can validate global errno to see the reason
|
||||
if (errno == ENOSPC) {
|
||||
NSError *enospcError = [NSError errorWithDomain:NSPOSIXErrorDomain
|
||||
code:ENOSPC
|
||||
userInfo:nil];
|
||||
unzippingError = enospcError;
|
||||
unzCloseCurrentFile(zip);
|
||||
success = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1190,7 +1190,8 @@ extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, in
|
||||
return UNZ_INTERNALERROR;
|
||||
#ifdef HAVE_AES
|
||||
if (s->cur_file_info.compression_method == AES_METHOD) {
|
||||
unsigned char passverify[AES_PWVERIFYSIZE];
|
||||
unsigned char passverify_archive[AES_PWVERIFYSIZE];
|
||||
unsigned char passverify_password[AES_PWVERIFYSIZE];
|
||||
unsigned char saltvalue[AES_MAXSALTLENGTH];
|
||||
uInt saltlength;
|
||||
|
||||
@@ -1202,11 +1203,14 @@ extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, in
|
||||
|
||||
if (ZREAD64(s->z_filefunc, s->filestream, saltvalue, saltlength) != saltlength)
|
||||
return UNZ_INTERNALERROR;
|
||||
if (ZREAD64(s->z_filefunc, s->filestream, passverify, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE)
|
||||
if (ZREAD64(s->z_filefunc, s->filestream, passverify_archive, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE)
|
||||
return UNZ_INTERNALERROR;
|
||||
|
||||
fcrypt_init((int)s->cur_file_info_internal.aes_encryption_mode, (unsigned char *)password, (unsigned int)strlen(password), saltvalue,
|
||||
passverify, &s->pfile_in_zip_read->aes_ctx);
|
||||
fcrypt_init(s->cur_file_info_internal.aes_encryption_mode, password, strlen(password), saltvalue,
|
||||
passverify_password, &s->pfile_in_zip_read->aes_ctx);
|
||||
|
||||
if (memcmp(passverify_archive, passverify_password, AES_PWVERIFYSIZE) != 0)
|
||||
return UNZ_BADPASSWORD;
|
||||
|
||||
pfile_in_zip_read_info->rest_read_compressed -= saltlength + AES_PWVERIFYSIZE;
|
||||
pfile_in_zip_read_info->rest_read_compressed -= AES_AUTHCODESIZE;
|
||||
|
||||
@@ -57,6 +57,7 @@ typedef voidp unzFile;
|
||||
#define UNZ_BADZIPFILE (-103)
|
||||
#define UNZ_INTERNALERROR (-104)
|
||||
#define UNZ_CRCERROR (-105)
|
||||
#define UNZ_BADPASSWORD (-106)
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
Reference in New Issue
Block a user