diff options
author | Mikkel Krautz <mikkel@krautz.dk> | 2012-03-21 18:42:54 +0400 |
---|---|---|
committer | Mikkel Krautz <mikkel@krautz.dk> | 2012-03-21 18:42:54 +0400 |
commit | 15c86c2a995951dc0a455e733204958d2a18341d (patch) | |
tree | 1eb1d70ec988465e05db4e81a1fb0b3fef38dcd3 | |
parent | a797defff1f8375005a027dee0af8fdb00ffe27c (diff) |
MUCertificate*: import all intermediates; show then in UI.
-rwxr-xr-x | Mumble.xcodeproj/project.pbxproj | 8 | ||||
m--------- | MumbleKit | 0 | ||||
-rw-r--r-- | Source/Classes/MUCertificateCell.h | 3 | ||||
-rw-r--r-- | Source/Classes/MUCertificateCell.m | 20 | ||||
-rw-r--r-- | Source/Classes/MUCertificateController.m | 30 | ||||
-rw-r--r-- | Source/Classes/MUCertificateDiskImportViewController.m | 20 | ||||
-rw-r--r-- | Source/Classes/MUCertificatePreferencesViewController.m | 87 | ||||
-rw-r--r-- | Source/Classes/MUCertificateViewController.m | 3 | ||||
-rw-r--r-- | Source/Classes/MUConnectionController.m | 2 |
9 files changed, 153 insertions, 20 deletions
diff --git a/Mumble.xcodeproj/project.pbxproj b/Mumble.xcodeproj/project.pbxproj index 306fc16..76b74be 100755 --- a/Mumble.xcodeproj/project.pbxproj +++ b/Mumble.xcodeproj/project.pbxproj @@ -42,6 +42,8 @@ 2861C2B2116BE9B7002B8514 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2861C2B1116BE9B7002B8514 /* CFNetwork.framework */; }; 2861C2B6116BE9BA002B8514 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2861C2B5116BE9BA002B8514 /* Security.framework */; }; 286E9260148452A000B13593 /* MUVoiceActivitySetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 286E925F148452A000B13593 /* MUVoiceActivitySetupViewController.m */; }; + 287172AB151A041A00153D74 /* certificatecell-intermediate.png in Resources */ = {isa = PBXBuildFile; fileRef = 287172A9151A041A00153D74 /* certificatecell-intermediate.png */; }; + 287172AC151A041A00153D74 /* certificatecell-intermediate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 287172AA151A041A00153D74 /* certificatecell-intermediate@2x.png */; }; 287639F911D29D99009DB8B6 /* MUCertificateCreationProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 287639F811D29D99009DB8B6 /* MUCertificateCreationProgressView.m */; }; 287639FF11D2A242009DB8B6 /* MUCertificateCreationProgressView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 287639FE11D2A242009DB8B6 /* MUCertificateCreationProgressView.xib */; }; 28763A4511D2AA91009DB8B6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28763A4411D2AA91009DB8B6 /* QuartzCore.framework */; }; @@ -263,6 +265,8 @@ 2861C2B5116BE9BA002B8514 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 286E925E148452A000B13593 /* MUVoiceActivitySetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MUVoiceActivitySetupViewController.h; sourceTree = "<group>"; }; 286E925F148452A000B13593 /* MUVoiceActivitySetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MUVoiceActivitySetupViewController.m; sourceTree = "<group>"; }; + 287172A9151A041A00153D74 /* certificatecell-intermediate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "certificatecell-intermediate.png"; path = "Resources/certificatecell-intermediate.png"; sourceTree = "<group>"; }; + 287172AA151A041A00153D74 /* certificatecell-intermediate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "certificatecell-intermediate@2x.png"; path = "Resources/certificatecell-intermediate@2x.png"; sourceTree = "<group>"; }; 287639F711D29D99009DB8B6 /* MUCertificateCreationProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MUCertificateCreationProgressView.h; sourceTree = "<group>"; }; 287639F811D29D99009DB8B6 /* MUCertificateCreationProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MUCertificateCreationProgressView.m; sourceTree = "<group>"; }; 287639FE11D2A242009DB8B6 /* MUCertificateCreationProgressView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MUCertificateCreationProgressView.xib; sourceTree = "<group>"; }; @@ -739,6 +743,8 @@ 28DC1C1B13DE596B0037CDC2 /* certificate512.png */, 28DC1C1C13DE596B0037CDC2 /* certificatecell-selected.png */, 28DC1C1D13DE596B0037CDC2 /* certificatecell-selected@2x.png */, + 287172A9151A041A00153D74 /* certificatecell-intermediate.png */, + 287172AA151A041A00153D74 /* certificatecell-intermediate@2x.png */, 28DC1C1E13DE596B0037CDC2 /* certificatecell.png */, 28DC1C1F13DE596B0037CDC2 /* certificatecell@2x.png */, 28DC1C2013DE596B0037CDC2 /* channel.png */, @@ -1073,6 +1079,8 @@ 285E2A69150D729B008AE185 /* Release.xcconfig in Resources */, 280256381510D70D00178B08 /* Localizable.strings in Resources */, 2802563C1510DDCA00178B08 /* Localizable.strings in Resources */, + 287172AB151A041A00153D74 /* certificatecell-intermediate.png in Resources */, + 287172AC151A041A00153D74 /* certificatecell-intermediate@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MumbleKit b/MumbleKit -Subproject bd4913ca76113977d1df931da025f2e81d16234 +Subproject f9f2957119e7090ce187d7338df65d9f8a22d8f diff --git a/Source/Classes/MUCertificateCell.h b/Source/Classes/MUCertificateCell.h index 47fcecb..cc32229 100644 --- a/Source/Classes/MUCertificateCell.h +++ b/Source/Classes/MUCertificateCell.h @@ -37,6 +37,9 @@ - (void) setIssuerText:(NSString *)issuerText; - (void) setExpiryText:(NSString *)expiryText; +- (BOOL) isIntermediate; +- (void) setIsIntermediate:(BOOL)isIntermediate; + - (BOOL) isCurrentCertificate; - (void) setIsCurrentCertificate:(BOOL)isSelected; diff --git a/Source/Classes/MUCertificateCell.m b/Source/Classes/MUCertificateCell.m index 37a445b..ccb4e80 100644 --- a/Source/Classes/MUCertificateCell.m +++ b/Source/Classes/MUCertificateCell.m @@ -38,6 +38,7 @@ IBOutlet UILabel *_issuerLabel; IBOutlet UILabel *_expiryLabel; BOOL _isCurrentCert; + BOOL _isIntermediate; } @end @@ -64,6 +65,19 @@ _expiryLabel.text = expiryText; } +- (void) setIsIntermediate:(BOOL)isIntermediate { + _isIntermediate = isIntermediate; + if (_isIntermediate) { + [_certImage setImage:[UIImage imageNamed:@"certificatecell-intermediate"]]; + } else { + [_certImage setImage:[UIImage imageNamed:@"certificatecell"]]; + } +} + +- (BOOL) isIntermediate { + return _isIntermediate; +} + - (void) setIsCurrentCertificate:(BOOL)isCurrent { _isCurrentCert = isCurrent; if (isCurrent) { @@ -71,7 +85,11 @@ [_nameLabel setTextColor:[MUColor selectedTextColor]]; [_emailLabel setTextColor:[MUColor selectedTextColor]]; } else { - [_certImage setImage:[UIImage imageNamed:@"certificatecell"]]; + if (_isIntermediate) { + [_certImage setImage:[UIImage imageNamed:@"certificatecell-intermediate"]]; + } else { + [_certImage setImage:[UIImage imageNamed:@"certificatecell"]]; + } [_nameLabel setTextColor:[UIColor blackColor]]; [_emailLabel setTextColor:[UIColor blackColor]]; } diff --git a/Source/Classes/MUCertificateController.m b/Source/Classes/MUCertificateController.m index 0988be6..523d8d5 100644 --- a/Source/Classes/MUCertificateController.m +++ b/Source/Classes/MUCertificateController.m @@ -40,14 +40,25 @@ kCFBooleanTrue, kSecReturnRef, kSecMatchLimitOne, kSecMatchLimit, nil]; - SecIdentityRef identity = NULL; + CFTypeRef thing = NULL; MKCertificate *cert = nil; - if (SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&identity) == noErr && identity != NULL) { - SecCertificateRef secCert; - if (SecIdentityCopyCertificate(identity, &secCert) == noErr) { + if (SecItemCopyMatching((CFDictionaryRef)query, &thing) == noErr && thing != NULL) { + CFTypeID receivedType = CFGetTypeID(thing); + if (receivedType == SecIdentityGetTypeID()) { + SecIdentityRef identity = (SecIdentityRef) thing; + SecCertificateRef secCert = NULL; + if (SecIdentityCopyCertificate(identity, &secCert) == noErr) { + NSData *secData = (NSData *)SecCertificateCopyData(secCert); + cert = [MKCertificate certificateWithCertificate:secData privateKey:nil]; + [secData release]; + } + } else if (receivedType == SecCertificateGetTypeID()) { + SecCertificateRef secCert = (SecCertificateRef) thing; NSData *secData = (NSData *)SecCertificateCopyData(secCert); cert = [MKCertificate certificateWithCertificate:secData privateKey:nil]; [secData release]; + } else { + return nil; } } return cert; @@ -60,13 +71,16 @@ kCFBooleanTrue, kSecReturnRef, kSecMatchLimitOne, kSecMatchLimit, nil]; - SecIdentityRef identity = NULL; + CFTypeRef thing = NULL; MKCertificate *cert = nil; - if (SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&identity) == noErr && identity != NULL) { + if (SecItemCopyMatching((CFDictionaryRef)query, &thing) == noErr && thing != NULL) { + // Only identities can have private keys. + if (CFGetTypeID(thing) != SecIdentityGetTypeID()) + return nil; SecCertificateRef secCert; - if (SecIdentityCopyCertificate(identity, &secCert) == noErr) { + if (SecIdentityCopyCertificate((SecIdentityRef) thing, &secCert) == noErr) { SecKeyRef secKey; - if (SecIdentityCopyPrivateKey(identity, &secKey) == noErr) { + if (SecIdentityCopyPrivateKey((SecIdentityRef) thing, &secKey) == noErr) { NSData *certData = (NSData *)SecCertificateCopyData(secCert); NSData *pkeyData = nil; query = [NSDictionary dictionaryWithObjectsAndKeys: diff --git a/Source/Classes/MUCertificateDiskImportViewController.m b/Source/Classes/MUCertificateDiskImportViewController.m index 5446d7c..e8cb718 100644 --- a/Source/Classes/MUCertificateDiskImportViewController.m +++ b/Source/Classes/MUCertificateDiskImportViewController.m @@ -205,9 +205,25 @@ static void ShowAlertDialog(NSString *title, NSString *msg) { NSArray *items = nil; OSStatus err = SecPKCS12Import((CFDataRef)transformedPkcs12Data, (CFDictionaryRef)dict, (CFArrayRef *)&items); - if (err == errSecSuccess && [items count] > 0) { + if (err == errSecSuccess && [items count] > 0) { + // Add all elements except the leaf certificate to the keychain NSDictionary *pkcsDict = [items objectAtIndex:0]; - // Get the SecIdentityRef + NSArray *chain = [pkcsDict objectForKey:(NSString *)kSecImportItemCertChain]; + for (int i = 1; i < [chain count]; i++) { + NSDictionary *op = [NSDictionary dictionaryWithObjectsAndKeys: + [chain objectAtIndex:i], kSecValueRef, nil]; + err = SecItemAdd((CFDictionaryRef)op, NULL); + if (err != noErr) { + if (err == errSecDuplicateItem) { + // Duplicates are OK in this case. + } else { + ShowAlertDialog(NSLocalizedString(@"Import Error", nil), + NSLocalizedString(@"Mumble was unable to import one of the intermediate certificates in the certificate chain.", nil)); + } + } + } + + // Get the SecIdentityRef, and add it to the keychain SecIdentityRef identity = (SecIdentityRef)[pkcsDict objectForKey:(id)kSecImportItemIdentity]; NSDictionary *op = [NSDictionary dictionaryWithObjectsAndKeys: (id)identity, kSecValueRef, diff --git a/Source/Classes/MUCertificatePreferencesViewController.m b/Source/Classes/MUCertificatePreferencesViewController.m index 26ab80d..c061b47 100644 --- a/Source/Classes/MUCertificatePreferencesViewController.m +++ b/Source/Classes/MUCertificatePreferencesViewController.m @@ -41,6 +41,7 @@ NSMutableArray *_certificateItems; BOOL _picker; NSUInteger _selectedIndex; + BOOL _showAll; } - (void) fetchCertificates; - (void) deleteCertificateForRow:(NSUInteger)row; @@ -54,6 +55,7 @@ - (id) init { if ((self = [super init])) { [self setContentSizeForViewInPopover:CGSizeMake(320, 480)]; + _showAll = [[[NSUserDefaults standardUserDefaults] objectForKey:@"CertificatesShowIntermediates"] boolValue]; } return self; } @@ -97,7 +99,6 @@ cell = [MUCertificateCell loadFromNib]; // Configure the cell... - cell.selectionStyle = UITableViewCellSelectionStyleGray; NSDictionary *dict = [_certificateItems objectAtIndex:[indexPath row]]; MKCertificate *cert = [dict objectForKey:@"cert"]; [cell setSubjectName:[cert subjectName]]; @@ -108,13 +109,21 @@ NSData *persistentRef = [dict objectForKey:@"persistentRef"]; NSData *curPersistentRef = [[NSUserDefaults standardUserDefaults] objectForKey:@"DefaultCertificate"]; + if ([[dict objectForKey:@"isIdentity"] boolValue]) { + [cell setIsIntermediate:NO]; + cell.selectionStyle = UITableViewCellSelectionStyleGray; + } else { + [cell setIsIntermediate:YES]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + } + if ([persistentRef isEqualToData:curPersistentRef]) { _selectedIndex = [indexPath row]; [cell setIsCurrentCertificate:YES]; } else { [cell setIsCurrentCertificate:NO]; } - + [cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton]; return (UITableViewCell *) cell; @@ -129,6 +138,12 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *dict = [_certificateItems objectAtIndex:[indexPath row]]; + + // Don't allow selection of intermediates. + if (![[dict objectForKey:@"isIdentity"] boolValue]) { + return; + } + NSData *persistentRef = [dict objectForKey:@"persistentRef"]; [[NSUserDefaults standardUserDefaults] setObject:persistentRef forKey:@"DefaultCertificate"]; @@ -164,12 +179,14 @@ #pragma mark Target/actions - (void) addButtonClicked:(UIBarButtonItem *)addButton { - NSString *title = NSLocalizedString(@"Add Certificate", @"Add certificate UIActionSheet"); - UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:title + NSString *showAllCerts = NSLocalizedString(@"Show All Certificates", nil); + NSString *showIdentities = NSLocalizedString(@"Show Identities Only", nil); + UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel", nil) destructiveButtonTitle:nil otherButtonTitles:NSLocalizedString(@"Generate New Certificate", nil), + _showAll ? showIdentities : showAllCerts, NSLocalizedString(@"Import From iTunes", nil), nil]; [sheet setActionSheetStyle:UIActionSheetStyleBlackOpaque]; @@ -189,7 +206,12 @@ [certGen release]; [[self navigationController] presentModalViewController:navCtrl animated:YES]; [navCtrl release]; - } else if (idx == 1) { // Import From Disk + } else if (idx == 1) { // Show All Certificates; Show Identities Only + _showAll = !_showAll; + [[NSUserDefaults standardUserDefaults] setBool:_showAll forKey:@"CertificatesShowIntermediates"]; + [self fetchCertificates]; + [self.tableView reloadData]; + } else if (idx == 2) { // Import From Disk MUCertificateDiskImportViewController *diskImportViewController = [[MUCertificateDiskImportViewController alloc] init]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:diskImportViewController]; [[self navigationController] presentModalViewController:navController animated:YES]; @@ -213,7 +235,60 @@ MKCertificate *cert = [MUCertificateController certificateWithPersistentRef:persistentRef]; if (cert) { [_certificateItems addObject:[NSDictionary dictionaryWithObjectsAndKeys: - cert, @"cert", persistentRef, @"persistentRef", nil]]; + cert, @"cert", + persistentRef, @"persistentRef", + [NSNumber numberWithBool:YES], @"isIdentity", + nil]]; + } + } + } + + if (_showAll) { + // Extract hashes of identity certs + NSMutableArray *identityCertHashes = [[[NSMutableArray alloc] init] autorelease]; + for (NSDictionary *item in _certificateItems) { + [identityCertHashes addObject:[[item objectForKey:@"cert"] digest]]; + } + + // Extract all intermediates + NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: + kSecClassCertificate, kSecClass, + kCFBooleanTrue, kSecReturnPersistentRef, + kSecMatchLimitAll, kSecMatchLimit, nil]; + NSArray *persistentRefs = nil; + SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&persistentRefs); + [persistentRefs autorelease]; + + for (NSData *ref in persistentRefs) { + NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: + ref, kSecValuePersistentRef, + kCFBooleanTrue, kSecReturnRef, + kSecMatchLimitOne, kSecMatchLimit, + nil]; + SecCertificateRef secCert; + if (SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&secCert) == noErr && secCert != NULL) { + NSData *certData = (NSData *) SecCertificateCopyData(secCert); + CFRelease(secCert); + + MKCertificate *consideredCert = [MKCertificate certificateWithCertificate:certData privateKey:nil]; + NSData *consideredDigest = [consideredCert digest]; + [certData release]; + + BOOL alreadyPresent = NO; + for (NSData *digest in identityCertHashes) { + if ([consideredDigest isEqualToData:digest]) { + alreadyPresent = YES; + break; + } + } + + if (!alreadyPresent) { + [_certificateItems addObject:[NSDictionary dictionaryWithObjectsAndKeys: + consideredCert, @"cert", + ref, @"persistentRef", + [NSNumber numberWithBool:NO], @"isIdentity", + nil]]; + } } } } diff --git a/Source/Classes/MUCertificateViewController.m b/Source/Classes/MUCertificateViewController.m index d684d0c..0e6320f 100644 --- a/Source/Classes/MUCertificateViewController.m +++ b/Source/Classes/MUCertificateViewController.m @@ -58,8 +58,7 @@ static const NSUInteger CertificateViewSectionTotal = 2; - (id) initWithPersistentRef:(NSData *)persistentRef { if ((self = [super initWithStyle:UITableViewStyleGrouped])) { - [MUCertificateController certificateWithPersistentRef:persistentRef]; - _certificates = [[NSArray arrayWithObject:[MUCertificateController certificateAndPrivateKeyWithPersistentRef:persistentRef]] retain]; + _certificates = [[NSArray arrayWithObject:[MUCertificateController certificateWithPersistentRef:persistentRef]] retain]; _allowExportAndDelete = YES; _persistentRef = [persistentRef retain]; diff --git a/Source/Classes/MUConnectionController.m b/Source/Classes/MUConnectionController.m index 5c3e0b2..3e9fad7 100644 --- a/Source/Classes/MUConnectionController.m +++ b/Source/Classes/MUConnectionController.m @@ -147,7 +147,7 @@ kSecMatchLimitOne, kSecMatchLimit, nil]; if (SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&secIdentity) == noErr && secIdentity != NULL) { - [_connection setClientIdentity:secIdentity]; + [_connection setCertificateChain:[NSArray arrayWithObject:(id)secIdentity]]; CFRelease(secIdentity); } } |