Skip to content

Commit

Permalink
More security tidbits!
Browse files Browse the repository at this point in the history
This patch prevents malicious downgrades, which are still possible with DSA validation: suppose there's some (signed) version with a security hole. A malicious attacker could serve an appcast with that version's URL and DSA signature, but a higher version number, forcing the user to "upgrade" to the version with the security hole.

While I was at it, I fixed a bug that should have completely stopped .pkg installation from working since 1.5b1. Why didn't I hear anything about that? Does anyone actually use .pkgs? It still needs testing to be sure it works.
  • Loading branch information
andymatuschak committed Sep 10, 2008
1 parent 95f9658 commit 9a0acca
Show file tree
Hide file tree
Showing 9 changed files with 31 additions and 20 deletions.
2 changes: 1 addition & 1 deletion SUBasicUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ - (void)installUpdate
if ([[NSFileManager defaultManager] copyPath:relaunchPathToCopy toPath:targetPath handler:nil])
relaunchPath = [targetPath retain];

[SUInstaller installFromUpdateFolder:[downloadPath stringByDeletingLastPathComponent] overHost:host delegate:self synchronously:[self shouldInstallSynchronously]];
[SUInstaller installFromUpdateFolder:[downloadPath stringByDeletingLastPathComponent] overHost:host delegate:self synchronously:[self shouldInstallSynchronously] versionComparator:[self _versionComparator]];
}

- (void)installerFinishedForHost:(SUHost *)aHost
Expand Down
1 change: 1 addition & 0 deletions SUConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ extern OSStatus SUMissingUpdateError;
extern OSStatus SUMissingInstallerToolError;
extern OSStatus SURelaunchError;
extern OSStatus SUInstallationError;
extern OSStatus SUDowngradeError;

// NSInteger is a type that was added to Leopard.
// Here is some glue to ensure that NSInteger will work with pre-10.5 SDKs:
Expand Down
1 change: 1 addition & 0 deletions SUConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@
OSStatus SUMissingInstallerToolError = 4003;
OSStatus SURelaunchError = 4004;
OSStatus SUInstallationError = 4005;
OSStatus SUDowngradeError = 4006;
3 changes: 2 additions & 1 deletion SUInstaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
#define SUINSTALLER_H

#import <Cocoa/Cocoa.h>
#import "SUVersionComparisonProtocol.h"

@class SUHost;
@interface SUInstaller : NSObject { }
+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously;
+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator;
+ (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate;
@end

Expand Down
8 changes: 2 additions & 6 deletions SUInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@
#import "SUPlainInstaller.h"
#import "SUPackageInstaller.h"

NSString *SUInstallerPathKey = @"SUInstallerPath";
NSString *SUInstallerHostKey = @"SUInstallerHost";
NSString *SUInstallerDelegateKey = @"SUInstallerDelegate";

@implementation SUInstaller

+ (BOOL)_isAliasFolderAtPath:(NSString *)path
Expand All @@ -36,7 +32,7 @@ + (BOOL)_isAliasFolderAtPath:(NSString *)path
}


+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously
+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator
{
// Search subdirectories for the application
NSString *currentFile, *newAppDownloadPath = nil, *bundleFileName = [[host bundlePath] lastPathComponent], *alternateBundleFileName = [[host name] stringByAppendingPathExtension:[[host bundlePath] pathExtension]];
Expand Down Expand Up @@ -76,7 +72,7 @@ + (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host
}
else
{
[(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationWithPath:newAppDownloadPath host:host delegate:delegate synchronously:synchronously];
[(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationWithPath:newAppDownloadPath host:host delegate:delegate synchronously:synchronously versionComparator:comparator];
}
}

Expand Down
1 change: 0 additions & 1 deletion SUPackageInstaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#import "SUPlainInstaller.h"

@interface SUPackageInstaller : SUPlainInstaller { }
+ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate;
@end

#endif
4 changes: 2 additions & 2 deletions SUPackageInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@implementation SUPackageInstaller

+ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate
+ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator;
{
NSError *error = nil;
BOOL result = YES;
Expand All @@ -26,7 +26,7 @@ + (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate
NSTask *installer = [NSTask launchedTaskWithLaunchPath:installerPath arguments:[NSArray arrayWithObjects:path, nil]];
[installer waitUntilExit];
// Known bug: if the installation fails or is canceled, Sparkle goes ahead and restarts, thinking everything is fine.
[self _finishInstallationWithResult:result host:bundle error:error delegate:delegate];
[self _finishInstallationWithResult:result host:host error:error delegate:delegate];
}

@end
3 changes: 2 additions & 1 deletion SUPlainInstaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@

#import "Sparkle.h"
#import "SUInstaller.h"
#import "SUVersionComparisonProtocol.h"

@interface SUPlainInstaller : SUInstaller { }
+ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously;
+ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator;
@end

#endif
28 changes: 20 additions & 8 deletions SUPlainInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,43 @@
#import "SUPlainInstaller.h"
#import "SUPlainInstallerInternals.h"

extern NSString *SUInstallerPathKey;
extern NSString *SUInstallerHostKey;
extern NSString *SUInstallerDelegateKey;
NSString *SUInstallerPathKey = @"SUInstallerPath";
NSString *SUInstallerHostKey = @"SUInstallerHost";
NSString *SUInstallerDelegateKey = @"SUInstallerDelegate";
NSString *SUInstallerVersionComparatorKey = @"SUInstallerVersionComparator";

@implementation SUPlainInstaller

+ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate
+ (void)installPath:(NSString *)path overHost:(SUHost *)bundle delegate:delegate versionComparator:(id <SUVersionComparison>)comparator
{
NSError *error;
BOOL result = [self copyPathWithAuthentication:path overPath:[bundle bundlePath] error:&error];
BOOL result = YES;

// Prevent malicious downgrades:
if ([comparator compareVersion:[bundle version] toVersion:[[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedDescending)
{
NSString * errorMessage = [NSString stringWithFormat:@"Sparkle Updater: Possible attack in progress! Attempting to \"upgrade\" from %@ to %@. Aborting update.", [bundle version], [[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:@"CFBundleVersion"]];
result = NO;
error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUDowngradeError userInfo:[NSDictionary dictionaryWithObject:errorMessage forKey:NSLocalizedDescriptionKey]];
}

if (result)
result = [self copyPathWithAuthentication:path overPath:[bundle bundlePath] error:&error];
[self _finishInstallationWithResult:result host:bundle error:error delegate:delegate];
}

+ (void)_performInstallationWithInfo:(NSDictionary *)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

[self installPath:[info objectForKey:SUInstallerPathKey] overHost:[info objectForKey:SUInstallerHostKey] delegate:[info objectForKey:SUInstallerDelegateKey]];
[self installPath:[info objectForKey:SUInstallerPathKey] overHost:[info objectForKey:SUInstallerHostKey] delegate:[info objectForKey:SUInstallerDelegateKey] versionComparator:[info objectForKey:SUInstallerVersionComparatorKey]];

[pool drain];
}

+ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously;
+ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator
{
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, SUInstallerPathKey, host, SUInstallerHostKey, delegate, SUInstallerDelegateKey, nil];
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, SUInstallerPathKey, host, SUInstallerHostKey, delegate, SUInstallerDelegateKey, comparator, SUInstallerVersionComparatorKey, nil];
if (synchronously)
[self _performInstallationWithInfo:info];
else
Expand Down

0 comments on commit 9a0acca

Please sign in to comment.