From 8a50f688fe9358387648fb83fbfecbefe8d32669 Mon Sep 17 00:00:00 2001 From: Apple Date: Sat, 3 Feb 2018 22:13:27 +0000 Subject: [PATCH] Security-58286.31.2.tar.gz --- Analytics/SFAnalyticsLogger.h | 2 +- Analytics/SFAnalyticsLogger.m | 257 +- Analytics/SQLite/SFSQLite.h | 7 +- Analytics/SQLite/SFSQLite.m | 265 +- Analytics/SQLite/SFSQLiteStatement.m | 69 +- CircleJoinRequested/CircleJoinRequested.m | 19 +- KeychainCircle/Tests/KCJoiningSessionTest.m | 29 +- KeychainCircle/Tests/KCPairingTest.m | 14 +- .../AppIcon.appiconset/Contents.json | 5 + .../KeychainSyncingOverIDSProxy+SendMessage.m | 3 + OSX/authd/authitems.c | 3 + OSX/authd/debugging.h | 7 +- OSX/authd/engine.c | 189 +- OSX/config/base.xcconfig | 3 +- OSX/config/security_macos.xcconfig | 3 +- .../en.lproj/authorization.prompts.strings | 2 +- .../lib/AuthorizationPriv.h | 4 +- .../lib/StaticCode.cpp | 2 +- .../lib/CertificateValues.cpp | 217 +- .../lib/CertificateValues.h | 25 +- OSX/libsecurity_keychain/lib/SecBase64P.c | 489 -- .../lib/SecCertificate.cpp | 19 +- .../lib/SecCertificateInternalP.h | 314 -- .../lib/SecCertificateP.c | 4762 ----------------- .../lib/SecCertificateP.h | 114 - .../lib/SecCertificatePrivP.h | 159 - OSX/libsecurity_keychain/lib/SecFrameworkP.c | 154 - OSX/libsecurity_keychain/lib/SecItem.cpp | 1 - OSX/libsecurity_keychain/lib/SecTrust.cpp | 2 - .../Tests/AppleMobilePersonalizedTicket.h | 129 - .../libDER/Tests/DER_Ticket.c | 203 - .../libDER/Tests/DER_Ticket.h | 96 - .../libDER/Tests/parseCert.c | 149 - .../libDER/Tests/parseCrl.c | 167 - .../libDER/Tests/parseTicket.c | 563 -- .../libDER/libDERUtils/fileIo.c | 85 - .../libDER/libDERUtils/fileIo.h | 29 - .../libDER/libDERUtils/libDERUtils.c | 57 - .../libDER/libDERUtils/printFields.c | 364 -- .../libDER/libDERUtils/printFields.h | 78 - .../regressions/si-20-sectrust-provisioning.c | 165 +- OSX/libsecurity_keychain/xpc-tsa/main-tsa.m | 2 +- OSX/libsecurity_smime/lib/tsaSupport.c | 42 +- .../Regressions/SOSRegressionUtilities.h | 15 +- .../Regressions/SOSRegressionUtilities.m | 35 +- .../Regressions/sc-130-resignationticket.c | 4 +- OSX/sec/SOSCircle/Regressions/sc-150-ring.m | 34 +- .../Regressions/sc-153-backupslicekeybag.c | 6 +- .../SOSCircle/Regressions/sc-20-keynames.m | 10 +- .../SOSCircle/Regressions/sc-30-peerinfo.c | 4 +- .../Regressions/sc-31-peerinfo-simplefuzz.c | 4 +- OSX/sec/SOSCircle/Regressions/sc-40-circle.c | 32 +- .../SOSCircle/SecureObjectSync/SOSAccount.m | 14 + .../SecureObjectSync/SOSAccountFullPeerInfo.m | 7 +- .../SecureObjectSync/SOSAccountGhost.m | 2 +- .../SecureObjectSync/SOSAccountPriv.h | 1 + .../SecureObjectSync/SOSAccountTransaction.m | 6 +- .../SOSAccountTrustClassic+Circle.m | 57 +- .../SOSAccountTrustClassic+Identity.h | 3 + .../SOSAccountTrustClassic+Identity.m | 94 +- .../SecureObjectSync/SOSCloudCircle.h | 4 +- .../SecureObjectSync/SOSCloudCircle.m | 20 +- OSX/sec/SOSCircle/SecureObjectSync/SOSCoder.c | 3 + .../SOSCircle/SecureObjectSync/SOSEngine.c | 1 + .../SecureObjectSync/SOSExports.exp-in | 1 + .../SecureObjectSync/SOSFullPeerInfo.h | 12 +- .../SecureObjectSync/SOSFullPeerInfo.m | 126 +- .../SOSCircle/SecureObjectSync/SOSPeerInfo.h | 27 +- .../SOSCircle/SecureObjectSync/SOSPeerInfo.m | 182 +- .../SecureObjectSync/SOSPeerInfoDER.m | 4 +- .../SecureObjectSync/SOSPeerInfoPriv.h | 2 +- .../SecureObjectSync/SOSTransportMessageIDS.m | 4 +- .../SecureObjectSync/SOSTransportMessageKVS.m | 3 + .../SOSCircle/SecureObjectSync/ViewList.list | 1 + OSX/sec/SOSCircle/Tool/recovery_key.h | 16 +- OSX/sec/SOSCircle/Tool/recovery_key.m | 15 +- .../Regressions/secitem/si-66-smime.c | 2 + OSX/sec/Security/SecCertificate.c | 149 +- OSX/sec/Security/SecCertificateInternal.h | 6 +- OSX/sec/Security/SecExports.exp-in | 1 + OSX/sec/Security/SecIdentity.c | 4 + OSX/sec/Security/SecItem.c | 2 +- OSX/sec/Security/SecPolicy.c | 2 +- OSX/sec/Security/SecRecoveryKey.m | 19 +- OSX/sec/ipc/server.c | 6 + OSX/sec/ipc/server_xpc.m | 63 +- .../securityd/Regressions/SOSAccountTesting.h | 71 +- .../secd-60-account-cloud-identity.m | 2 +- .../securityd/Regressions/secd-668-ghosts.m | 32 +- OSX/sec/securityd/SOSCloudCircleServer.h | 3 + OSX/sec/securityd/SOSCloudCircleServer.m | 36 +- OSX/sec/securityd/SecCertificateServer.c | 17 + OSX/sec/securityd/SecCertificateServer.h | 2 + OSX/sec/securityd/SecCertificateSource.c | 12 +- OSX/sec/securityd/SecItemSchema.c | 132 + OSX/sec/securityd/SecItemServer.c | 2 +- OSX/sec/securityd/SecPinningDb.m | 2 +- OSX/sec/securityd/SecRevocationDb.c | 9 +- OSX/sec/securityd/SecRevocationNetworking.m | 2 +- OSX/sec/securityd/SecTrustServer.c | 43 +- OSX/sec/securityd/SecTrustServer.h | 3 +- ...eDODIDSWCA37-noRequireExplicitPolicies.cer | Bin .../FakeDODIDSWCA37.cer | Bin .../FakeDODInteroperability-extraPolicies.cer | Bin ...roperability-noRequireExplicitPolicies.cer | Bin .../FakeDODInteroperability.cer | Bin .../FakeDODLeaf.cer | Bin ...keDODRootCA3-noRequireExplicitPolicies.cer | Bin .../FakeDODRootCA3.cer | Bin .../FakeFederalBridgeCA-extraPolicies.cer | Bin .../FakeFederalBridgeCA.cer | Bin .../FakeFederalCommonPolicy.cer | Bin OSX/trustd/trustd.c | 10 + RegressionTests/manifeststresstest/Keychain.m | 2 +- RegressionTests/manifeststresstest/Monkey.m | 2 +- RegressionTests/manifeststresstest/mark.m | 4 +- Security.exp-in | 6 +- Security.xcodeproj/project.pbxproj | 869 +-- .../xcshareddata/xcschemes/CKKSTests.xcscheme | 1 + .../xcschemes/osx - secdtests.xcscheme | 4 +- keychain/SecAccessControl.h | 22 +- keychain/SecItemPriv.h | 1 + keychain/SecKey.h | 2 +- keychain/ckks/CKKS.h | 74 +- keychain/ckks/CKKS.m | 71 +- keychain/ckks/CKKSAnalyticsLogger.h | 5 +- keychain/ckks/CKKSAnalyticsLogger.m | 76 +- keychain/ckks/CKKSCKAccountStateTracker.h | 6 +- keychain/ckks/CKKSCKAccountStateTracker.m | 6 +- keychain/ckks/CKKSControl.h | 54 + keychain/ckks/CKKSControl.m | 199 + keychain/ckks/CKKSControlProtocol.m | 12 +- keychain/ckks/CKKSCurrentItemPointer.h | 1 + keychain/ckks/CKKSCurrentItemPointer.m | 6 +- keychain/ckks/CKKSCurrentKeyPointer.h | 4 + keychain/ckks/CKKSCurrentKeyPointer.m | 3 + .../CKKSFetchAllRecordZoneChangesOperation.m | 3 +- keychain/ckks/CKKSFixups.h | 57 + keychain/ckks/CKKSFixups.m | 272 + keychain/ckks/CKKSGroupOperation.h | 52 +- keychain/ckks/CKKSGroupOperation.m | 352 +- keychain/ckks/CKKSHealKeyHierarchyOperation.m | 58 +- .../ckks/CKKSHealTLKSharesOperation.h | 41 +- keychain/ckks/CKKSHealTLKSharesOperation.m | 211 + keychain/ckks/CKKSIncomingQueueEntry.h | 4 +- keychain/ckks/CKKSIncomingQueueEntry.m | 4 +- keychain/ckks/CKKSIncomingQueueOperation.m | 4 +- keychain/ckks/CKKSItem.h | 4 +- keychain/ckks/CKKSItem.m | 8 +- keychain/ckks/CKKSKey.h | 11 +- keychain/ckks/CKKSKey.m | 60 +- keychain/ckks/CKKSKeychainView.h | 33 +- keychain/ckks/CKKSKeychainView.m | 821 ++- keychain/ckks/CKKSLockStateTracker.m | 3 +- keychain/ckks/CKKSManifest.m | 2 +- keychain/ckks/CKKSMirrorEntry.h | 4 +- keychain/ckks/CKKSMirrorEntry.m | 3 +- keychain/ckks/CKKSNewTLKOperation.m | 30 +- keychain/ckks/CKKSOutgoingQueueEntry.m | 66 +- keychain/ckks/CKKSOutgoingQueueOperation.m | 14 +- keychain/ckks/CKKSPeer.h | 97 + keychain/ckks/CKKSPeer.m | 132 + .../ckks/CKKSProcessReceivedKeysOperation.m | 4 +- keychain/ckks/CKKSRecordHolder.h | 4 +- keychain/ckks/CKKSRecordHolder.m | 4 +- keychain/ckks/CKKSResultOperation.h | 72 + keychain/ckks/CKKSResultOperation.m | 184 + keychain/ckks/CKKSSIV.h | 4 + keychain/ckks/CKKSSIV.m | 16 + keychain/ckks/CKKSSQLDatabaseObject.m | 10 +- keychain/ckks/CKKSScanLocalItemsOperation.m | 6 +- keychain/ckks/CKKSTLKShare.h | 105 + keychain/ckks/CKKSTLKShare.m | 616 +++ .../CKKSUpdateCurrentItemPointerOperation.h | 1 + .../CKKSUpdateCurrentItemPointerOperation.m | 69 +- .../ckks/CKKSUpdateDeviceStateOperation.m | 11 +- keychain/ckks/CKKSViewManager.h | 18 +- keychain/ckks/CKKSViewManager.m | 244 +- keychain/ckks/CKKSZone.h | 4 + keychain/ckks/CKKSZone.m | 30 +- keychain/ckks/CKKSZoneChangeFetcher.h | 3 + keychain/ckks/CKKSZoneChangeFetcher.m | 7 + keychain/ckks/CKKSZoneStateEntry.h | 11 +- keychain/ckks/CKKSZoneStateEntry.m | 30 +- keychain/ckks/CloudKitCategories.h | 10 + keychain/ckks/CloudKitCategories.m | 23 + keychain/ckks/CloudKitDependencies.h | 37 + keychain/ckks/NSOperationCategories.h | 49 + keychain/ckks/NSOperationCategories.m | 127 + keychain/ckks/proto/CKKSSerializedKey.proto | 13 + .../ckks/proto/source/CKKSSerializedKey.h | 44 + .../ckks/proto/source/CKKSSerializedKey.m | 193 + ...ionTests.m => CKKSAESSIVEncryptionTests.m} | 93 +- keychain/ckks/tests/CKKSCloudKitTests.m | 2 + keychain/ckks/tests/CKKSLoggerTests.m | 72 +- keychain/ckks/tests/CKKSManifestTests.m | 18 +- keychain/ckks/tests/CKKSOperationTests.m | 153 +- keychain/ckks/tests/CKKSSOSTests.m | 61 +- keychain/ckks/tests/CKKSSQLTests.m | 41 +- .../tests/CKKSServerValidationRecoveryTests.m | 267 + .../tests/CKKSTLKSharingEncryptionTests.m | 299 ++ keychain/ckks/tests/CKKSTLKSharingTests.m | 541 ++ keychain/ckks/tests/CKKSTests+API.m | 188 +- .../ckks/tests/CKKSTests+CurrentPointerAPI.m | 164 +- keychain/ckks/tests/CKKSTests.m | 254 +- .../tests/CloudKitKeychainSyncingFixupTests.m | 282 + .../tests/CloudKitKeychainSyncingMockXCTest.h | 21 + .../tests/CloudKitKeychainSyncingMockXCTest.m | 266 +- keychain/ckks/tests/CloudKitMockXCTest.h | 32 +- keychain/ckks/tests/CloudKitMockXCTest.m | 176 +- keychain/ckks/tests/MockCloudKit.h | 10 +- keychain/ckks/tests/MockCloudKit.m | 184 +- keychain/ckksctl/ckksctl.h | 18 - keychain/ckksctl/ckksctl.m | 217 +- .../securityd_service/main.c | 15 +- trust/SecCertificatePriv.h | 11 + trust/SecPolicyPriv.h | 2 +- xcconfig/PlatformLibraries.xcconfig | 5 +- xcconfig/Security.xcconfig | 3 +- xcconfig/Version.xcconfig | 9 + ...ork_requiring_modern_objc_runtime.xcconfig | 4 +- 221 files changed, 9415 insertions(+), 10367 deletions(-) delete mode 100644 OSX/libsecurity_keychain/lib/SecBase64P.c delete mode 100644 OSX/libsecurity_keychain/lib/SecCertificateInternalP.h delete mode 100644 OSX/libsecurity_keychain/lib/SecCertificateP.c delete mode 100644 OSX/libsecurity_keychain/lib/SecCertificateP.h delete mode 100644 OSX/libsecurity_keychain/lib/SecCertificatePrivP.h delete mode 100644 OSX/libsecurity_keychain/lib/SecFrameworkP.c delete mode 100644 OSX/libsecurity_keychain/libDER/Tests/AppleMobilePersonalizedTicket.h delete mode 100644 OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.c delete mode 100644 OSX/libsecurity_keychain/libDER/Tests/DER_Ticket.h delete mode 100644 OSX/libsecurity_keychain/libDER/Tests/parseCert.c delete mode 100644 OSX/libsecurity_keychain/libDER/Tests/parseCrl.c delete mode 100644 OSX/libsecurity_keychain/libDER/Tests/parseTicket.c delete mode 100644 OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.c delete mode 100644 OSX/libsecurity_keychain/libDER/libDERUtils/fileIo.h delete mode 100644 OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.c delete mode 100644 OSX/libsecurity_keychain/libDER/libDERUtils/printFields.c delete mode 100644 OSX/libsecurity_keychain/libDER/libDERUtils/printFields.h mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODIDSWCA37-noRequireExplicitPolicies.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODIDSWCA37.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODInteroperability-extraPolicies.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODInteroperability-noRequireExplicitPolicies.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODInteroperability.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODLeaf.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODRootCA3-noRequireExplicitPolicies.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeDODRootCA3.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeFederalBridgeCA-extraPolicies.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeFederalBridgeCA.cer mode change 100755 => 100644 OSX/shared_regressions/si-20-sectrust-policies-data/FakeFederalCommonPolicy.cer create mode 100644 keychain/ckks/CKKSControl.h create mode 100644 keychain/ckks/CKKSControl.m create mode 100644 keychain/ckks/CKKSFixups.h create mode 100644 keychain/ckks/CKKSFixups.m rename OSX/libsecurity_keychain/libDER/libDERUtils/libDERUtils.h => keychain/ckks/CKKSHealTLKSharesOperation.h (65%) create mode 100644 keychain/ckks/CKKSHealTLKSharesOperation.m create mode 100644 keychain/ckks/CKKSPeer.h create mode 100644 keychain/ckks/CKKSPeer.m create mode 100644 keychain/ckks/CKKSResultOperation.h create mode 100644 keychain/ckks/CKKSResultOperation.m create mode 100644 keychain/ckks/CKKSTLKShare.h create mode 100644 keychain/ckks/CKKSTLKShare.m create mode 100644 keychain/ckks/NSOperationCategories.h create mode 100644 keychain/ckks/NSOperationCategories.m create mode 100644 keychain/ckks/proto/CKKSSerializedKey.proto create mode 100644 keychain/ckks/proto/source/CKKSSerializedKey.h create mode 100644 keychain/ckks/proto/source/CKKSSerializedKey.m rename keychain/ckks/tests/{CKKSEncryptionTests.m => CKKSAESSIVEncryptionTests.m} (87%) create mode 100644 keychain/ckks/tests/CKKSServerValidationRecoveryTests.m create mode 100644 keychain/ckks/tests/CKKSTLKSharingEncryptionTests.m create mode 100644 keychain/ckks/tests/CKKSTLKSharingTests.m create mode 100644 keychain/ckks/tests/CloudKitKeychainSyncingFixupTests.m delete mode 100644 keychain/ckksctl/ckksctl.h create mode 100644 xcconfig/Version.xcconfig diff --git a/Analytics/SFAnalyticsLogger.h b/Analytics/SFAnalyticsLogger.h index ebdba8b7..6dfed9ff 100644 --- a/Analytics/SFAnalyticsLogger.h +++ b/Analytics/SFAnalyticsLogger.h @@ -54,7 +54,7 @@ // Things below are for utilities to drive and/or test the system - (NSString*)getSysdiagnoseDumpWithError:(NSError**)error; -- (NSData*)getLoggingJSONWithError:(NSError**)error; +- (NSData*)getLoggingJSON:(bool)pretty error:(NSError**)error; - (BOOL)forceUploadWithError:(NSError**)error; // -------------------------------- diff --git a/Analytics/SFAnalyticsLogger.m b/Analytics/SFAnalyticsLogger.m index 2a750ca5..2bc376f9 100644 --- a/Analytics/SFAnalyticsLogger.m +++ b/Analytics/SFAnalyticsLogger.m @@ -28,6 +28,8 @@ #import "CKKSViewManager.h" #import "debugging.h" #import +#import +#import NSString* const SFAnalyticsLoggerTableSuccessCount = @"success_count"; NSString* const SFAnalyticsLoggerColumnEventType = @"event_type"; @@ -47,9 +49,13 @@ NSString* const SFAnalyticsLoggerSplunkTopic = @"topic"; NSString* const SFAnalyticsLoggerSplunkEventTime = @"eventTime"; NSString* const SFAnalyticsLoggerSplunkPostTime = @"postTime"; NSString* const SFAnalyticsLoggerSplunkEventType = @"eventType"; +NSString* const SFAnalyticsLoggerSplunkEventBuild = @"build"; +NSString* const SFAnalyticsLoggerSplunkEventProduct = @"product"; + NSString* const SFAnalyticsLoggerMetricsBase = @"metricsBase"; NSString* const SFAnalyticsLoggerEventClassKey = @"eventClass"; + NSString* const SFAnalyticsUserDefaultsSuite = @"com.apple.security.analytics"; static NSString* const SFAnalyticsLoggerTableSchema = @"CREATE TABLE IF NOT EXISTS hard_failures (\n" @@ -89,15 +95,16 @@ static NSString* const SFAnalyticsLoggerTableSchema = @"CREATE TABLE IF NOT EXIS #define SFANALYTICS_SPLUNK_DEV 0 #define SFANALYTICS_MAX_EVENTS_TO_REPORT 999 +#define SECONDS_PER_DAY (60 * 60 * 24) + #if SFANALYTICS_SPLUNK_DEV -#define SECONDS_BETWEEN_UPLOADS 10 +#define SECONDS_BETWEEN_UPLOADS_CUSTOMER 10 +#define SECONDS_BETWEEN_UPLOADS_INTERNAL 10 #else -// three days = 60 seconds times 60 minutes * 72 hours -#define SECONDS_BETWEEN_UPLOADS (60 * 60 * 72) +#define SECONDS_BETWEEN_UPLOADS_CUSTOMER (3 * SECONDS_PER_DAY) +#define SECONDS_BETWEEN_UPLOADS_INTERNAL (SECONDS_PER_DAY) #endif -#define SECONDS_PER_DAY (60 * 60 * 24) - typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { SFAnalyticsEventClassSuccess, SFAnalyticsEventClassHardFailure, @@ -121,6 +128,7 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { - (NSInteger)softFailureCountForEventType:(NSString*)eventType; - (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table; - (void)clearAllData; +- (BOOL)tryToOpenDatabase; - (NSDictionary*)summaryCounts; @@ -201,7 +209,12 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { if (self = [super init]) { _database = [SFAnalyticsLoggerSQLiteStore storeWithPath:self.class.databasePath schema:SFAnalyticsLoggerTableSchema]; _queue = dispatch_queue_create("com.apple.security.analytics", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); - _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS; + + if (os_variant_has_internal_diagnostics("Security")) { + _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS_INTERNAL; + } else { + _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS_CUSTOMER; + } NSDictionary* systemDefaultValues = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle bundleWithPath:@"/System/Library/Frameworks/Security.framework"] pathForResource:@"SFAnalyticsLogging" ofType:@"plist"]]; _splunkTopicName = systemDefaultValues[@"splunk_topic"]; @@ -256,12 +269,12 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { - (void)logHardFailureForEventNamed:(NSString*)eventName withAttributes:(NSDictionary*)attributes { - [self logEventNamed:eventName class:SFAnalyticsEventClassSoftFailure attributes:attributes]; + [self logEventNamed:eventName class:SFAnalyticsEventClassHardFailure attributes:attributes]; } - (void)logSoftFailureForEventNamed:(NSString*)eventName withAttributes:(NSDictionary*)attributes { - [self logEventNamed:eventName class:SFAnalyticsEventClassHardFailure attributes:attributes]; + [self logEventNamed:eventName class:SFAnalyticsEventClassSoftFailure attributes:attributes]; } - (void)noteEventNamed:(NSString*)eventName @@ -401,8 +414,11 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { dispatch_semaphore_t sem = dispatch_semaphore_create(0); __block NSError* error = nil; - NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; - NSURLSession* storeBagSession = [NSURLSession sessionWithConfiguration:defaultConfiguration + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + + configuration.HTTPAdditionalHeaders = @{ @"User-Agent" : [NSString stringWithFormat:@"securityd/%s", SECURITY_BUILD_VERSION]}; + + NSURLSession* storeBagSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; @@ -475,7 +491,7 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { - (BOOL)forceUploadWithError:(NSError**)error { __block BOOL result = NO; - NSData* json = [self getLoggingJSONWithError:error]; + NSData* json = [self getLoggingJSON:false error: error]; dispatch_sync(_queue, ^{ if (json && [self _onQueuePostJSON:json error:error]) { secinfo("ckks", "uploading sync health data: %@", json); @@ -500,8 +516,11 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { * Create the NSURLSession * We use the ephemeral session config because we don't need cookies or cache */ - NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; - NSURLSession* postSession = [NSURLSession sessionWithConfiguration:defaultConfiguration + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + + configuration.HTTPAdditionalHeaders = @{ @"User-Agent" : [NSString stringWithFormat:@"securityd/%s", SECURITY_BUILD_VERSION]}; + + NSURLSession* postSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; @@ -605,7 +624,6 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { - (NSString*)getSysdiagnoseDumpWithError:(NSError**)error { NSMutableString* sysdiagnose = [[NSMutableString alloc] init]; - NSDictionary* extraValues = self.extraValuesToUploadToServer; [extraValues enumerateKeysAndObjectsUsingBlock:^(NSString* key, id object, BOOL* stop) { [sysdiagnose appendFormat:@"Key: %@, Value: %@\n", key, object]; @@ -623,11 +641,39 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { return sysdiagnose; } -- (NSData*)getLoggingJSONWithError:(NSError**)error ++ (void)addOSVersion:(NSMutableDictionary *)event +{ + static dispatch_once_t onceToken; + static NSString *build = NULL; + static NSString *product = NULL; + dispatch_once(&onceToken, ^{ + NSDictionary *version = CFBridgingRelease(_CFCopySystemVersionDictionary()); + if (version == NULL) + return; + build = version[(__bridge NSString *)_kCFSystemVersionBuildVersionKey]; + product = version[(__bridge NSString *)_kCFSystemVersionProductNameKey]; + }); + if (build) + event[SFAnalyticsLoggerSplunkEventBuild] = build; + if (product) + event[SFAnalyticsLoggerSplunkEventProduct] = product; +} + +- (NSData*)getLoggingJSON:(bool)pretty error:(NSError**)error { __block NSData* json = nil; NSDictionary* extraValues = self.extraValuesToUploadToServer; dispatch_sync(_queue, ^{ + if (![self->_database tryToOpenDatabase]) { + // we should not even be here because uploadDate was nil. But since we are, let's get out of here. + // Returning nil here will abort the upload (but again, the uploadDate should've done that already) + secerror("can't get logging JSON because database is not openable"); + if (error) { + *error = [NSError errorWithDomain:@"SFAnalyticsLogger" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"could not open db to read and process metrics (device in class D?)"}]; + } + return; + } + NSArray* failureRecords = self->_database.failureRecords; NSDictionary* successCounts = self->_database.summaryCounts; @@ -649,13 +695,19 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { healthSummaryEvent[SFAnalyticsLoggerColumnSuccessCount] = @(totalSuccessCount); healthSummaryEvent[SFAnalyticsLoggerColumnHardFailureCount] = @(totalHardFailureCount); healthSummaryEvent[SFAnalyticsLoggerColumnSoftFailureCount] = @(totalSoftFailureCount); + [SFAnalyticsLogger addOSVersion:healthSummaryEvent]; NSMutableArray* splunkRecords = failureRecords.mutableCopy; [splunkRecords addObject:healthSummaryEvent]; - NSDictionary* jsonDict = @{SFAnalyticsLoggerSplunkPostTime : @([now timeIntervalSince1970] * 1000), @"events" : splunkRecords}; + NSDictionary* jsonDict = @{ + SFAnalyticsLoggerSplunkPostTime : @([now timeIntervalSince1970] * 1000), + @"events" : splunkRecords + }; - json = [NSJSONSerialization dataWithJSONObject:jsonDict options:NSJSONWritingPrettyPrinted error:error]; + json = [NSJSONSerialization dataWithJSONObject:jsonDict + options:(pretty ? NSJSONWritingPrettyPrinted : 0) + error:error]; }); return json; @@ -746,7 +798,11 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { loggingStores[standardizedPath] = store; } - [store open]; + NSError* error = nil; + if (![store openWithError:&error]) { + secerror("SFAnalyticsLogger: could not open db at init, will try again later. Error: %@", error); + } + } return store; @@ -757,120 +813,171 @@ typedef NS_ENUM(NSInteger, SFAnalyticsEventClass) { [self close]; } +- (BOOL)tryToOpenDatabase +{ + if (!self.isOpen) { + secwarning("SFAnalyticsLogger: db is closed, attempting to open"); + NSError* error = nil; + if (![self openWithError:&error]) { + secerror("SFAnalyticsLogger: failed to open db with error %@", error); + return NO; + } + } + return YES; +} + - (NSInteger)successCountForEventType:(NSString*)eventType { - return [[[[self select:@[SFAnalyticsLoggerColumnSuccessCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSuccessCount] integerValue]; + if ([self tryToOpenDatabase]) { + return [[[[self select:@[SFAnalyticsLoggerColumnSuccessCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSuccessCount] integerValue]; + } + return 0; } - (void)incrementSuccessCountForEventType:(NSString*)eventType { - NSInteger successCount = [self successCountForEventType:eventType]; - NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType]; - NSInteger softFailureCount = [self softFailureCountForEventType:eventType]; - [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount + 1), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}]; + if ([self tryToOpenDatabase]) { + NSInteger successCount = [self successCountForEventType:eventType]; + NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType]; + NSInteger softFailureCount = [self softFailureCountForEventType:eventType]; + [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount + 1), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}]; + } } - (NSInteger)hardFailureCountForEventType:(NSString*)eventType { - return [[[[self select:@[SFAnalyticsLoggerColumnHardFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnHardFailureCount] integerValue]; + if ([self tryToOpenDatabase]) { + return [[[[self select:@[SFAnalyticsLoggerColumnHardFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnHardFailureCount] integerValue]; + } + return 0; } - (NSInteger)softFailureCountForEventType:(NSString*)eventType { - return [[[[self select:@[SFAnalyticsLoggerColumnSoftFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSoftFailureCount] integerValue]; + if ([self tryToOpenDatabase]) { + return [[[[self select:@[SFAnalyticsLoggerColumnSoftFailureCount] from:SFAnalyticsLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsLoggerColumnSoftFailureCount] integerValue]; + } + return 0; } - (void)incrementHardFailureCountForEventType:(NSString*)eventType { - NSInteger successCount = [self successCountForEventType:eventType]; - NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType]; - NSInteger softFailureCount = [self softFailureCountForEventType:eventType]; - [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount + 1), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}]; + if ([self tryToOpenDatabase]) { + NSInteger successCount = [self successCountForEventType:eventType]; + NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType]; + NSInteger softFailureCount = [self softFailureCountForEventType:eventType]; + [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount + 1), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount)}]; + } } - (void)incrementSoftFailureCountForEventType:(NSString*)eventType { - NSInteger successCount = [self successCountForEventType:eventType]; - NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType]; - NSInteger softFailureCount = [self softFailureCountForEventType:eventType]; - [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount + 1)}]; + if ([self tryToOpenDatabase]) { + NSInteger successCount = [self successCountForEventType:eventType]; + NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType]; + NSInteger softFailureCount = [self softFailureCountForEventType:eventType]; + [self insertOrReplaceInto:SFAnalyticsLoggerTableSuccessCount values:@{SFAnalyticsLoggerColumnEventType : eventType, SFAnalyticsLoggerColumnSuccessCount : @(successCount), SFAnalyticsLoggerColumnHardFailureCount : @(hardFailureCount), SFAnalyticsLoggerColumnSoftFailureCount : @(softFailureCount + 1)}]; + } } - (NSDictionary*)summaryCounts { - NSMutableDictionary* successCountsDict = [NSMutableDictionary dictionary]; - NSArray* rows = [self selectAllFrom:SFAnalyticsLoggerTableSuccessCount where:nil bindings:nil]; - for (NSDictionary* rowDict in rows) { - NSString* eventName = rowDict[SFAnalyticsLoggerColumnEventType]; - if (!eventName) { - secinfo("SFAnalytics", "ignoring entry in success counts table without an event name"); - continue; - } + if ([self tryToOpenDatabase]) { + NSMutableDictionary* successCountsDict = [NSMutableDictionary dictionary]; + NSArray* rows = [self selectAllFrom:SFAnalyticsLoggerTableSuccessCount where:nil bindings:nil]; + for (NSDictionary* rowDict in rows) { + NSString* eventName = rowDict[SFAnalyticsLoggerColumnEventType]; + if (!eventName) { + secinfo("SFAnalytics", "ignoring entry in success counts table without an event name"); + continue; + } - successCountsDict[eventName] = @{SFAnalyticsLoggerTableSuccessCount : rowDict[SFAnalyticsLoggerColumnSuccessCount], SFAnalyticsLoggerColumnHardFailureCount : rowDict[SFAnalyticsLoggerColumnHardFailureCount], SFAnalyticsLoggerColumnSoftFailureCount : rowDict[SFAnalyticsLoggerColumnSoftFailureCount]}; + successCountsDict[eventName] = @{SFAnalyticsLoggerTableSuccessCount : rowDict[SFAnalyticsLoggerColumnSuccessCount], SFAnalyticsLoggerColumnHardFailureCount : rowDict[SFAnalyticsLoggerColumnHardFailureCount], SFAnalyticsLoggerColumnSoftFailureCount : rowDict[SFAnalyticsLoggerColumnSoftFailureCount]}; + } + return successCountsDict; } - - return successCountsDict; + return [NSDictionary new]; } - (NSArray*)failureRecords { - NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableHardFailures]; - if (recordBlobs.count < SFANALYTICS_MAX_EVENTS_TO_REPORT) { - NSArray* softFailureBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableSoftFailures]; - if (softFailureBlobs.count > 0) { - NSInteger numSoftFailuresToReport = SFANALYTICS_MAX_EVENTS_TO_REPORT - recordBlobs.count; - recordBlobs = [recordBlobs arrayByAddingObjectsFromArray:[softFailureBlobs subarrayWithRange:NSMakeRange(softFailureBlobs.count - numSoftFailuresToReport, numSoftFailuresToReport)]]; + if ([self tryToOpenDatabase]) { + NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableHardFailures]; + if (recordBlobs.count < SFANALYTICS_MAX_EVENTS_TO_REPORT) { + NSArray* softFailureBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableSoftFailures]; + if (softFailureBlobs.count > 0) { + NSUInteger numSoftFailuresToReport = SFANALYTICS_MAX_EVENTS_TO_REPORT - recordBlobs.count; + if (numSoftFailuresToReport > softFailureBlobs.count) + numSoftFailuresToReport = softFailureBlobs.count; + + recordBlobs = [recordBlobs arrayByAddingObjectsFromArray:[softFailureBlobs subarrayWithRange:NSMakeRange(softFailureBlobs.count - numSoftFailuresToReport, numSoftFailuresToReport)]]; + } } - } - NSMutableArray* failureRecords = [[NSMutableArray alloc] init]; - for (NSDictionary* row in recordBlobs) { - NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:0 format:nil error:nil]; - [failureRecords addObject:deserializedRecord]; + NSMutableArray* failureRecords = [[NSMutableArray alloc] init]; + for (NSDictionary* row in recordBlobs) { + NSMutableDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:NSPropertyListMutableContainers format:nil error:nil]; + [SFAnalyticsLogger addOSVersion:deserializedRecord]; + [failureRecords addObject:deserializedRecord]; + } + return failureRecords; } - - return failureRecords; + return [NSArray new]; } - (NSArray*)allEvents { - NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableAllEvents]; - NSMutableArray* records = [[NSMutableArray alloc] init]; - for (NSDictionary* row in recordBlobs) { - NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:0 format:nil error:nil]; - [records addObject:deserializedRecord]; + if ([self tryToOpenDatabase]) { + NSArray* recordBlobs = [self select:@[SFAnalyticsLoggerColumnData] from:SFAnalyticsLoggerTableAllEvents]; + NSMutableArray* records = [[NSMutableArray alloc] init]; + for (NSDictionary* row in recordBlobs) { + NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsLoggerColumnData] options:0 format:nil error:nil]; + [records addObject:deserializedRecord]; + } + return records; } - return records; + return [NSArray new]; } - (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table { - NSError* error = nil; - NSData* serializedRecord = [NSPropertyListSerialization dataWithPropertyList:eventDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error]; - if(!error && serializedRecord) { - [self insertOrReplaceInto:table values:@{SFAnalyticsLoggerColumnDate : [NSDate date], SFAnalyticsLoggerColumnData : serializedRecord}]; - } - if(error && !serializedRecord) { - secerror("Couldn't serialize failure record: %@", error); + if ([self tryToOpenDatabase]) { + NSError* error = nil; + NSData* serializedRecord = [NSPropertyListSerialization dataWithPropertyList:eventDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error]; + if(!error && serializedRecord) { + [self insertOrReplaceInto:table values:@{SFAnalyticsLoggerColumnDate : [NSDate date], SFAnalyticsLoggerColumnData : serializedRecord}]; + } + if(error && !serializedRecord) { + secerror("Couldn't serialize failure record: %@", error); + } } } +// the other returning methods give default values in case of closed db, +// but this needs to be nil so the comparison to 'now' fails and we don't upload - (NSDate*)uploadDate { - return [self datePropertyForKey:SFAnalyticsLoggerUploadDate]; + if ([self tryToOpenDatabase]) { + return [self datePropertyForKey:SFAnalyticsLoggerUploadDate]; + } + return nil; } - (void)setUploadDate:(NSDate*)uploadDate { - [self setDateProperty:uploadDate forKey:SFAnalyticsLoggerUploadDate]; + if ([self tryToOpenDatabase]) { + [self setDateProperty:uploadDate forKey:SFAnalyticsLoggerUploadDate]; + } } - (void)clearAllData { - [self deleteFrom:SFAnalyticsLoggerTableSuccessCount where:@"event_type like ?" bindings:@[@"%"]]; - [self deleteFrom:SFAnalyticsLoggerTableHardFailures where:@"id >= 0" bindings:nil]; - [self deleteFrom:SFAnalyticsLoggerTableSoftFailures where:@"id >= 0" bindings:nil]; + if ([self tryToOpenDatabase]) { + [self deleteFrom:SFAnalyticsLoggerTableSuccessCount where:@"event_type like ?" bindings:@[@"%"]]; + [self deleteFrom:SFAnalyticsLoggerTableHardFailures where:@"id >= 0" bindings:nil]; + [self deleteFrom:SFAnalyticsLoggerTableSoftFailures where:@"id >= 0" bindings:nil]; + [self deleteFrom:SFAnalyticsLoggerTableAllEvents where:@"id >= 0" bindings:nil]; + } } @end diff --git a/Analytics/SQLite/SFSQLite.h b/Analytics/SQLite/SFSQLite.h index 9a5a4b20..cb6a80df 100644 --- a/Analytics/SQLite/SFSQLite.h +++ b/Analytics/SQLite/SFSQLite.h @@ -103,9 +103,6 @@ typedef NS_ENUM(NSInteger, SFSQLiteSynchronousMode) { - (void)analyze; - (void)vacuum; -// Raise an exception. Including any database error in the description and removing the databse if it's corrupt. -- (void)raise:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); - // The rowID assigned to the last record inserted into the database. - (SFSQLiteRowID)lastInsertRowID; @@ -113,8 +110,8 @@ typedef NS_ENUM(NSInteger, SFSQLiteSynchronousMode) { - (int)changes; // Execute one-or-more queries. Use prepared statements for anything performance critical. -- (void)executeSQL:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); -- (void)executeSQL:(NSString *)format arguments:(va_list)args NS_FORMAT_FUNCTION(1, 0); +- (BOOL)executeSQL:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); +- (BOOL)executeSQL:(NSString *)format arguments:(va_list)args NS_FORMAT_FUNCTION(1, 0); // Prepared statement pool accessors. Statements must be reset after they're used. - (SFSQLiteStatement *)statementForSQL:(NSString *)SQL; diff --git a/Analytics/SQLite/SFSQLite.m b/Analytics/SQLite/SFSQLite.m index 4590e889..f83444e1 100644 --- a/Analytics/SQLite/SFSQLite.m +++ b/Analytics/SQLite/SFSQLite.m @@ -25,7 +25,7 @@ #import "SFSQLiteStatement.h" #include #include - +#import "debugging.h" #define kSFSQLiteBusyTimeout (5*60*1000) @@ -42,10 +42,6 @@ static NSString *const kSFSQLiteCreatePropertiesTableSQL = @" value text\n" @");\n"; -@interface SFSQLiteError : NSObject -+ (void)raise:(NSString *)reason code:(int)code extended:(int)extended; -@end - NSArray *SFSQLiteJournalSuffixes() { return @[@"-journal", @"-wal", @"-shm"]; @@ -339,9 +335,15 @@ allDone: } // You don't argue with the Ben: rdar://12685305 - [self executeSQL:@"pragma journal_mode = WAL"]; - [self executeSQL:@"pragma synchronous = %@", [self _synchronousModeString]]; - [self executeSQL:@"pragma auto_vacuum = FULL"]; + if (![self executeSQL:@"pragma journal_mode = WAL"]) { + goto done; + } + if (![self executeSQL:@"pragma synchronous = %@", [self _synchronousModeString]]) { + goto done; + } + if (![self executeSQL:@"pragma auto_vacuum = FULL"]) { + goto done; + } // rdar://problem/32168789 // [self executeSQL:@"pragma foreign_keys = 1"]; @@ -410,7 +412,15 @@ allDone: success = YES; done: + if (!success) { + sqlite3_close_v2(_db); + _db = nil; + } + if (!success && error) { + if (!localError) { + localError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Error opening db at %@, ", _path]}]; + } *error = localError; } return success; @@ -419,7 +429,8 @@ done: - (void)open { NSError *error; if (![self openWithError:&error]) { - [self raise:@"Error opening db at %@: %@", self.path, error]; + secerror("sfsqlite: Error opening db at %@: %@", self.path, error); + return; } } @@ -432,7 +443,8 @@ done: [self removeAllStatements]; if (sqlite3_close(_db)) { - [self raise:@"Error closing database"]; + secerror("sfsqlite: Error closing database"); + return; } _db = NULL; } @@ -468,44 +480,10 @@ done: [self executeSQL:@"vacuum"]; } -- (void)raise:(NSString *)format, ... { - va_list args; - va_start(args, format); - - NSString *reason = [[NSString alloc] initWithFormat:format arguments:args]; - - int code = 0; - int extendedCode = 0; - if (_db) { - code = sqlite3_errcode(_db) & 0xFF; - extendedCode = sqlite3_extended_errcode(_db); - const char *errmsg = sqlite3_errmsg(_db); - - NSDictionary *dbAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:NULL]; - NSDictionary *fsAttrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:self.path error:NULL]; - reason = [reason stringByAppendingFormat:@" - errcode:%04x, msg:\"%s\", size: %@, path:%@, fs:%@/%@", extendedCode, errmsg, dbAttrs[NSFileSize], _path, fsAttrs[NSFileSystemFreeSize], fsAttrs[NSFileSystemSize]]; - - if (!_corrupt && (code == SQLITE_CORRUPT || code == SQLITE_NOTADB)) { - _corrupt = YES; - - @try { - [self close]; - } @catch (NSException *x) { - NSLog(@"Warn: Error closing corrupt db: %@", x); - } - - [self remove]; - } - } - - va_end(args); - - [SFSQLiteError raise:reason code:code extended:extendedCode]; -} - - (SFSQLiteRowID)lastInsertRowID { if (!_db) { - [self raise:@"Database is closed"]; + secerror("sfsqlite: Database is closed"); + return -1; } return sqlite3_last_insert_rowid(_db); @@ -514,33 +492,40 @@ done: - (int)changes { if (!_db) { - [self raise:@"Database is closed"]; + secerror("sfsqlite: Database is closed"); + return -1; } return sqlite3_changes(_db); } -- (void)executeSQL:(NSString *)format, ... { +- (BOOL)executeSQL:(NSString *)format, ... { va_list args; va_start(args, format); - [self executeSQL:format arguments:args]; + BOOL result = [self executeSQL:format arguments:args]; va_end(args); + return result; } -- (void)executeSQL:(NSString *)format arguments:(va_list)args { +- (BOOL)executeSQL:(NSString *)format arguments:(va_list)args { NS_VALID_UNTIL_END_OF_SCOPE NSString *SQL = [[NSString alloc] initWithFormat:format arguments:args]; if (!_db) { - [self raise:@"Database is closed"]; + secerror("sfsqlite: Database is closed"); + return NO; } int execRet = sqlite3_exec(_db, [SQL UTF8String], NULL, NULL, NULL); if (execRet != SQLITE_OK) { - [self raise:@"Error executing SQL: \"%@\" (%d)", SQL, execRet]; + secerror("sfsqlite: Error executing SQL: \"%@\" (%d)", SQL, execRet); + return NO; } + + return YES; } - (SFSQLiteStatement *)statementForSQL:(NSString *)SQL { if (!_db) { - [self raise:@"Database is closed"]; + secerror("sfsqlite: Database is closed"); + return nil; } SFSQLiteStatement *statement = _statementsBySQL[SQL]; @@ -550,7 +535,8 @@ done: sqlite3_stmt *handle = NULL; NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeSQL = SQL; if (sqlite3_prepare_v2(_db, [arcSafeSQL UTF8String], -1, &handle, NULL)) { - [self raise:@"Error preparing statement: %@", SQL]; + secerror("Error preparing statement: %@", SQL); + return nil; } statement = [[SFSQLiteStatement alloc] initWithSQLite:self SQL:SQL handle:handle]; @@ -880,7 +866,8 @@ done: - (NSString *)_tableNameForClass:(Class)objectClass { NSString *className = [objectClass SFSQLiteClassName]; if (![className hasPrefix:_objectClassPrefix]) { - [NSException raise:NSInvalidArgumentException format:@"Object class \"%@\" does not have prefix \"%@\"", className, _objectClassPrefix]; + secerror("sfsqlite: %@", [NSString stringWithFormat:@"Object class \"%@\" does not have prefix \"%@\"", className, _objectClassPrefix]); + return nil; } return [className substringFromIndex:_objectClassPrefix.length]; } @@ -897,171 +884,3 @@ done: } @end - - -#define SFSQLiteErrorRaiseMethod(SQLiteError) + (void)SQLiteError:(NSString *)reason { [NSException raise:NSGenericException format:@"%@", reason]; } -#define SFSQLiteErrorCase(SQLiteError) case SQLITE_ ## SQLiteError: [self SQLiteError:reason]; break - -@implementation SFSQLiteError - -// SQLite error codes -SFSQLiteErrorRaiseMethod(ERROR) -SFSQLiteErrorRaiseMethod(INTERNAL) -SFSQLiteErrorRaiseMethod(PERM) -SFSQLiteErrorRaiseMethod(ABORT) -SFSQLiteErrorRaiseMethod(BUSY) -SFSQLiteErrorRaiseMethod(LOCKED) -SFSQLiteErrorRaiseMethod(NOMEM) -SFSQLiteErrorRaiseMethod(READONLY) -SFSQLiteErrorRaiseMethod(INTERRUPT) -SFSQLiteErrorRaiseMethod(IOERR) -SFSQLiteErrorRaiseMethod(CORRUPT) -SFSQLiteErrorRaiseMethod(NOTFOUND) -SFSQLiteErrorRaiseMethod(FULL) -SFSQLiteErrorRaiseMethod(CANTOPEN) -SFSQLiteErrorRaiseMethod(PROTOCOL) -SFSQLiteErrorRaiseMethod(SCHEMA) -SFSQLiteErrorRaiseMethod(TOOBIG) -SFSQLiteErrorRaiseMethod(CONSTRAINT) -SFSQLiteErrorRaiseMethod(MISMATCH) -SFSQLiteErrorRaiseMethod(MISUSE) -SFSQLiteErrorRaiseMethod(RANGE) -SFSQLiteErrorRaiseMethod(NOTADB) - -// SQLite extended error codes -SFSQLiteErrorRaiseMethod(IOERR_READ) -SFSQLiteErrorRaiseMethod(IOERR_SHORT_READ) -SFSQLiteErrorRaiseMethod(IOERR_WRITE) -SFSQLiteErrorRaiseMethod(IOERR_FSYNC) -SFSQLiteErrorRaiseMethod(IOERR_DIR_FSYNC) -SFSQLiteErrorRaiseMethod(IOERR_TRUNCATE) -SFSQLiteErrorRaiseMethod(IOERR_FSTAT) -SFSQLiteErrorRaiseMethod(IOERR_UNLOCK) -SFSQLiteErrorRaiseMethod(IOERR_RDLOCK) -SFSQLiteErrorRaiseMethod(IOERR_DELETE) -SFSQLiteErrorRaiseMethod(IOERR_BLOCKED) -SFSQLiteErrorRaiseMethod(IOERR_NOMEM) -SFSQLiteErrorRaiseMethod(IOERR_ACCESS) -SFSQLiteErrorRaiseMethod(IOERR_CHECKRESERVEDLOCK) -SFSQLiteErrorRaiseMethod(IOERR_LOCK) -SFSQLiteErrorRaiseMethod(IOERR_CLOSE) -SFSQLiteErrorRaiseMethod(IOERR_DIR_CLOSE) -SFSQLiteErrorRaiseMethod(IOERR_SHMOPEN) -SFSQLiteErrorRaiseMethod(IOERR_SHMSIZE) -SFSQLiteErrorRaiseMethod(IOERR_SHMLOCK) -SFSQLiteErrorRaiseMethod(IOERR_SHMMAP) -SFSQLiteErrorRaiseMethod(IOERR_SEEK) -SFSQLiteErrorRaiseMethod(IOERR_DELETE_NOENT) -SFSQLiteErrorRaiseMethod(IOERR_MMAP) -SFSQLiteErrorRaiseMethod(IOERR_GETTEMPPATH) -SFSQLiteErrorRaiseMethod(IOERR_CONVPATH) -SFSQLiteErrorRaiseMethod(LOCKED_SHAREDCACHE) -SFSQLiteErrorRaiseMethod(BUSY_RECOVERY) -SFSQLiteErrorRaiseMethod(BUSY_SNAPSHOT) -SFSQLiteErrorRaiseMethod(CANTOPEN_NOTEMPDIR) -SFSQLiteErrorRaiseMethod(CANTOPEN_ISDIR) -SFSQLiteErrorRaiseMethod(CANTOPEN_FULLPATH) -SFSQLiteErrorRaiseMethod(CANTOPEN_CONVPATH) -SFSQLiteErrorRaiseMethod(CORRUPT_VTAB) -SFSQLiteErrorRaiseMethod(READONLY_RECOVERY) -SFSQLiteErrorRaiseMethod(READONLY_CANTLOCK) -SFSQLiteErrorRaiseMethod(READONLY_ROLLBACK) -SFSQLiteErrorRaiseMethod(READONLY_DBMOVED) -SFSQLiteErrorRaiseMethod(ABORT_ROLLBACK) -SFSQLiteErrorRaiseMethod(CONSTRAINT_CHECK) -SFSQLiteErrorRaiseMethod(CONSTRAINT_COMMITHOOK) -SFSQLiteErrorRaiseMethod(CONSTRAINT_FOREIGNKEY) -SFSQLiteErrorRaiseMethod(CONSTRAINT_FUNCTION) -SFSQLiteErrorRaiseMethod(CONSTRAINT_NOTNULL) -SFSQLiteErrorRaiseMethod(CONSTRAINT_PRIMARYKEY) -SFSQLiteErrorRaiseMethod(CONSTRAINT_TRIGGER) -SFSQLiteErrorRaiseMethod(CONSTRAINT_UNIQUE) -SFSQLiteErrorRaiseMethod(CONSTRAINT_VTAB) -SFSQLiteErrorRaiseMethod(CONSTRAINT_ROWID) -SFSQLiteErrorRaiseMethod(NOTICE_RECOVER_WAL) -SFSQLiteErrorRaiseMethod(NOTICE_RECOVER_ROLLBACK) - -+ (void)raise:(NSString *)reason code:(int)code extended:(int)extended { - switch(extended) { - SFSQLiteErrorCase(IOERR_READ); - SFSQLiteErrorCase(IOERR_SHORT_READ); - SFSQLiteErrorCase(IOERR_WRITE); - SFSQLiteErrorCase(IOERR_FSYNC); - SFSQLiteErrorCase(IOERR_DIR_FSYNC); - SFSQLiteErrorCase(IOERR_TRUNCATE); - SFSQLiteErrorCase(IOERR_FSTAT); - SFSQLiteErrorCase(IOERR_UNLOCK); - SFSQLiteErrorCase(IOERR_RDLOCK); - SFSQLiteErrorCase(IOERR_DELETE); - SFSQLiteErrorCase(IOERR_BLOCKED); - SFSQLiteErrorCase(IOERR_NOMEM); - SFSQLiteErrorCase(IOERR_ACCESS); - SFSQLiteErrorCase(IOERR_CHECKRESERVEDLOCK); - SFSQLiteErrorCase(IOERR_LOCK); - SFSQLiteErrorCase(IOERR_CLOSE); - SFSQLiteErrorCase(IOERR_DIR_CLOSE); - SFSQLiteErrorCase(IOERR_SHMOPEN); - SFSQLiteErrorCase(IOERR_SHMSIZE); - SFSQLiteErrorCase(IOERR_SHMLOCK); - SFSQLiteErrorCase(IOERR_SHMMAP); - SFSQLiteErrorCase(IOERR_SEEK); - SFSQLiteErrorCase(IOERR_DELETE_NOENT); - SFSQLiteErrorCase(IOERR_MMAP); - SFSQLiteErrorCase(IOERR_GETTEMPPATH); - SFSQLiteErrorCase(IOERR_CONVPATH); - SFSQLiteErrorCase(LOCKED_SHAREDCACHE); - SFSQLiteErrorCase(BUSY_RECOVERY); - SFSQLiteErrorCase(BUSY_SNAPSHOT); - SFSQLiteErrorCase(CANTOPEN_NOTEMPDIR); - SFSQLiteErrorCase(CANTOPEN_ISDIR); - SFSQLiteErrorCase(CANTOPEN_FULLPATH); - SFSQLiteErrorCase(CANTOPEN_CONVPATH); - SFSQLiteErrorCase(CORRUPT_VTAB); - SFSQLiteErrorCase(READONLY_RECOVERY); - SFSQLiteErrorCase(READONLY_CANTLOCK); - SFSQLiteErrorCase(READONLY_ROLLBACK); - SFSQLiteErrorCase(READONLY_DBMOVED); - SFSQLiteErrorCase(ABORT_ROLLBACK); - SFSQLiteErrorCase(CONSTRAINT_CHECK); - SFSQLiteErrorCase(CONSTRAINT_COMMITHOOK); - SFSQLiteErrorCase(CONSTRAINT_FOREIGNKEY); - SFSQLiteErrorCase(CONSTRAINT_FUNCTION); - SFSQLiteErrorCase(CONSTRAINT_NOTNULL); - SFSQLiteErrorCase(CONSTRAINT_PRIMARYKEY); - SFSQLiteErrorCase(CONSTRAINT_TRIGGER); - SFSQLiteErrorCase(CONSTRAINT_UNIQUE); - SFSQLiteErrorCase(CONSTRAINT_VTAB); - SFSQLiteErrorCase(CONSTRAINT_ROWID); - SFSQLiteErrorCase(NOTICE_RECOVER_WAL); - SFSQLiteErrorCase(NOTICE_RECOVER_ROLLBACK); - default: break; - } - switch(code) { - SFSQLiteErrorCase(ERROR); - SFSQLiteErrorCase(INTERNAL); - SFSQLiteErrorCase(PERM); - SFSQLiteErrorCase(ABORT); - SFSQLiteErrorCase(BUSY); - SFSQLiteErrorCase(LOCKED); - SFSQLiteErrorCase(NOMEM); - SFSQLiteErrorCase(READONLY); - SFSQLiteErrorCase(INTERRUPT); - SFSQLiteErrorCase(IOERR); - SFSQLiteErrorCase(CORRUPT); - SFSQLiteErrorCase(NOTFOUND); - SFSQLiteErrorCase(FULL); - SFSQLiteErrorCase(CANTOPEN); - SFSQLiteErrorCase(PROTOCOL); - SFSQLiteErrorCase(SCHEMA); - SFSQLiteErrorCase(TOOBIG); - SFSQLiteErrorCase(CONSTRAINT); - SFSQLiteErrorCase(MISMATCH); - SFSQLiteErrorCase(MISUSE); - SFSQLiteErrorCase(RANGE); - SFSQLiteErrorCase(NOTADB); - default: break; - } - [NSException raise:NSGenericException format:@"%@", reason]; -} - -@end diff --git a/Analytics/SQLite/SFSQLiteStatement.m b/Analytics/SQLite/SFSQLiteStatement.m index c8de43a8..a142bc9f 100644 --- a/Analytics/SQLite/SFSQLiteStatement.m +++ b/Analytics/SQLite/SFSQLiteStatement.m @@ -24,6 +24,7 @@ #import "SFSQLite.h" #import "SFSQLiteStatement.h" #import "SFObjCType.h" +#import "debugging.h" @interface SFSQLiteStatement () @property (nonatomic, strong) NSMutableArray *temporaryBoundObjects; @@ -47,13 +48,13 @@ } - (void)finalizeStatement { - SFSQLite *strongSQLite = _SQLite; - if (!_reset) { - [strongSQLite raise: @"Statement not reset after last use: \"%@\"", _SQL]; + secerror("sfsqlite: Statement not reset after last use: \"%@\"", _SQL); + return; } if (sqlite3_finalize(_handle)) { - [strongSQLite raise:@"Error finalizing prepared statement: \"%@\"", _SQL]; + secerror("sfsqlite: Error finalizing prepared statement: \"%@\"", _SQL); + return; } } @@ -79,21 +80,21 @@ return NO; } else { [self resetAfterStepError]; - [_SQLite raise:@"Failed to step (%d): \"%@\"", rc, _SQL]; + secerror("sfsqlite: Failed to step (%d): \"%@\"", rc, _SQL); return NO; } } - (void)reset { - SFSQLite *strongSQLite = _SQLite; - if (!_reset) { if (sqlite3_reset(_handle)) { - [strongSQLite raise:@"Error resetting prepared statement: \"%@\"", _SQL]; + secerror("sfsqlite: Error resetting prepared statement: \"%@\"", _SQL); + return; } if (sqlite3_clear_bindings(_handle)) { - [strongSQLite raise:@"Error clearing prepared statement bindings: \"%@\"", _SQL]; + secerror("sfsqlite: Error clearing prepared statement bindings: \"%@\"", _SQL); + return; } [_temporaryBoundObjects removeAllObjects]; _reset = YES; @@ -101,36 +102,52 @@ } - (void)bindInt:(SInt32)value atIndex:(NSUInteger)index { - NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL); + if (!_reset) { + secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL); + return; + } if (sqlite3_bind_int(_handle, (int)index+1, value)) { - [_SQLite raise:@"Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL]; + secerror("sfsqlite: Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL); + return; } } - (void)bindInt64:(SInt64)value atIndex:(NSUInteger)index { - NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL); + if (!_reset) { + secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL); + return; + } if (sqlite3_bind_int64(_handle, (int)index+1, value)) { - [_SQLite raise:@"Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL]; + secerror("sfsqlite: Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL); + return; } } - (void)bindDouble:(double)value atIndex:(NSUInteger)index { - NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL); + if (!_reset) { + secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL); + return; + } if (sqlite3_bind_double(_handle, (int)index+1, value)) { - [_SQLite raise:@"Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL]; + secerror("sfsqlite: Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL); + return; } } - (void)bindBlob:(NSData *)value atIndex:(NSUInteger)index { - NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL); + if (!_reset) { + secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL); + return; + } if (value) { NS_VALID_UNTIL_END_OF_SCOPE NSData *arcSafeValue = value; if (sqlite3_bind_blob(_handle, (int)index+1, [arcSafeValue bytes], (int)[arcSafeValue length], NULL)) { - [_SQLite raise:@"Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL]; + secerror("sfsqlite: Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL); + return; } } else { [self bindNullAtIndex:index]; @@ -138,12 +155,16 @@ } - (void)bindText:(NSString *)value atIndex:(NSUInteger)index { - NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL); - + if (!_reset) { + secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL); + return; + } + if (value) { NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeValue = value; if (sqlite3_bind_text(_handle, (int)index+1, [arcSafeValue UTF8String], -1, NULL)) { - [_SQLite raise:@"Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL]; + secerror("sfsqlite: Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL); + return; } } else { [self bindNullAtIndex:index]; @@ -153,7 +174,8 @@ - (void)bindNullAtIndex:(NSUInteger)index { int rc = sqlite3_bind_null(_handle, (int)index+1); if ((rc & 0x00FF) != SQLITE_OK) { - [_SQLite raise:@"sqlite3_bind_null error"]; + secerror("sfsqlite: sqlite3_bind_null error"); + return; } } @@ -201,7 +223,8 @@ } else if ([value isKindOfClass:[NSURL class]]) { [self bindText:[self retainedTemporaryBoundObject:[value absoluteString]] atIndex:index]; } else { - [NSException raise:NSInvalidArgumentException format:@"Can't bind object of type %@", [value class]]; + secerror("sfsqlite: Can't bind object of type %@", [value class]); + return; } } @@ -289,7 +312,7 @@ return nil; default: - [NSException raise:NSGenericException format:@"Unexpected column type: %d", type]; + secerror("sfsqlite: Unexpected column type: %d", type); return nil; } } diff --git a/CircleJoinRequested/CircleJoinRequested.m b/CircleJoinRequested/CircleJoinRequested.m index d6942e57..19f2a4b6 100644 --- a/CircleJoinRequested/CircleJoinRequested.m +++ b/CircleJoinRequested/CircleJoinRequested.m @@ -60,6 +60,7 @@ #include "utilities/debugging.h" #include "utilities/SecAKSWrappers.h" #include "utilities/SecCFWrappers.h" +#include #import "CoreCDP/CDPFollowUpController.h" #import "CoreCDP/CDPFollowUpContext.h" @@ -122,7 +123,11 @@ static void keybagDidUnlock() } SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&error); - if(_isAccountICDP && (circleStatus == kSOSCCError || circleStatus == kSOSCCCircleAbsent || circleStatus == kSOSCCNotInCircle) && _hasPostedFollowupAndStillInError == false){ + if (circleStatus == kSOSCCError && error && CFEqual(sSecXPCErrorDomain, CFErrorGetDomain(error))) { + secnotice("cjr", "unable to determine circle status due to xpc failure: %@", error); + return; + } + else if (_isAccountICDP && (circleStatus == kSOSCCError || circleStatus == kSOSCCCircleAbsent || circleStatus == kSOSCCNotInCircle) && _hasPostedFollowupAndStillInError == false) { NSError *localError = nil; CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair]; CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init]; @@ -749,9 +754,19 @@ static bool processEvents() CFErrorRef error = NULL; CFErrorRef departError = NULL; SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&error); + enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&departError); + + // Error due to XPC failure does not provide information about the circle. + if (circleStatus == kSOSCCError && error && (CFEqual(sSecXPCErrorDomain, CFErrorGetDomain(error)))) { + secnotice("cjr", "XPC error while checking circle status: \"%@\", not processing events", error); + return true; + } else if (departureReason == kSOSDepartureReasonError && departError && (CFEqual(sSecXPCErrorDomain, CFErrorGetDomain(departError)))) { + secnotice("cjr", "XPC error while checking last departure reason: \"%@\", not processing events", departError); + return true; + } + NSDate *nowish = [NSDate date]; PersistentState *state = [PersistentState loadFromStorage]; - enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&departError); secnotice("cjr", "CircleStatus %d -> %d{%d} (s=%p)", state.lastCircleStatus, circleStatus, departureReason, state); // Pending application reminder diff --git a/KeychainCircle/Tests/KCJoiningSessionTest.m b/KeychainCircle/Tests/KCJoiningSessionTest.m index 4f95e3c5..7a437f07 100644 --- a/KeychainCircle/Tests/KCJoiningSessionTest.m +++ b/KeychainCircle/Tests/KCJoiningSessionTest.m @@ -21,12 +21,14 @@ __unused static SOSFullPeerInfoRef SOSNSFullPeerInfoCreate(NSDictionary* gestalt, - NSData* backupKey, SecKeyRef signingKey, SecKeyRef octagonSigningKey, - NSError**error) + NSData* backupKey, SecKeyRef signingKey, + SecKeyRef octagonSigningKey, + SecKeyRef octagonEncryptionKey, + NSError**error) { CFErrorRef errorRef = NULL; - SOSFullPeerInfoRef result = SOSFullPeerInfoCreate(NULL, (__bridge CFDictionaryRef) gestalt, (__bridge CFDataRef) backupKey, signingKey, octagonSigningKey, &errorRef); + SOSFullPeerInfoRef result = SOSFullPeerInfoCreate(NULL, (__bridge CFDictionaryRef) gestalt, (__bridge CFDataRef) backupKey, signingKey, octagonSigningKey, octagonEncryptionKey, &errorRef); if (errorRef && error) { *error = (__bridge_transfer NSError*) errorRef; @@ -54,24 +56,6 @@ static SecKeyRef GenerateFullECKey(int keySize, NSError** error) { } -__unused static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(NSString* name, SecKeyRef* outSigningKey, SecKeyRef* outOctagonSigningKey, NSError** error) -{ - if (outSigningKey == NULL || outOctagonSigningKey == NULL) - return NULL; - - *outSigningKey = GenerateFullECKey(256, error); - if (*outSigningKey == NULL) - return NULL; - - *outOctagonSigningKey = GenerateFullECKey(384, error); - if (*outOctagonSigningKey == NULL) { - return NULL; - } - - return SOSNSFullPeerInfoCreate(@{(__bridge NSString*)kPIUserDefinedDeviceNameKey:name}, nil, *outSigningKey, *outOctagonSigningKey, error); -} - - @interface KCJoiningRequestTestDelegate : NSObject @property (readwrite) NSString* sharedSecret; @@ -120,8 +104,9 @@ __unused static SOSFullPeerInfoRef SOSCreateFullPeerInfoFromName(NSString* name, SecKeyRef signingKey = GenerateFullECKey(256, NULL); SecKeyRef octagonSigningKey = GenerateFullECKey(384, NULL); + SecKeyRef octagonEncryptionKey = GenerateFullECKey(384, NULL); - self.peerInfo = SOSPeerInfoCreate(NULL, (__bridge CFDictionaryRef) @{(__bridge NSString*)kPIUserDefinedDeviceNameKey:@"Fakey"}, NULL, signingKey, octagonSigningKey, NULL); + self.peerInfo = SOSPeerInfoCreate(NULL, (__bridge CFDictionaryRef) @{(__bridge NSString*)kPIUserDefinedDeviceNameKey:@"Fakey"}, NULL, signingKey, octagonSigningKey, octagonEncryptionKey, NULL); if (self.peerInfo == NULL) return nil; diff --git a/KeychainCircle/Tests/KCPairingTest.m b/KeychainCircle/Tests/KCPairingTest.m index eb77f2dd..7e106ada 100644 --- a/KeychainCircle/Tests/KCPairingTest.m +++ b/KeychainCircle/Tests/KCPairingTest.m @@ -50,6 +50,7 @@ @property (assign) SecKeyRef accountPublicKey; @property (assign) SecKeyRef deviceKey; @property (assign) SecKeyRef octagonSigningKey; +@property (assign) SecKeyRef octagonEncryptionKey; @property (assign) SOSCircleRef circle; @property (assign) SOSFullPeerInfoRef fullPeerInfo; @property (assign) bool application; @@ -90,6 +91,13 @@ } CFReleaseNull(publicKey); + if(SecKeyGeneratePair((__bridge CFDictionaryRef)octagonParameters, &publicKey, &_octagonEncryptionKey) != 0) { + NSLog(@"failed to create octagon signing key"); + return nil; + } + CFReleaseNull(publicKey); + + _circle = (SOSCircleRef)CFRetain(circle); CFErrorRef error = NULL; @@ -98,7 +106,7 @@ @"ComputerName" : @"name", }; - _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, &error); + _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, _octagonEncryptionKey, &error); CFReleaseNull(error); if (randomAccountKey) { @@ -138,6 +146,10 @@ SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonSigningKey }); CFReleaseNull(_octagonSigningKey); } + if (_octagonEncryptionKey) { + SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonEncryptionKey }); + CFReleaseNull(_octagonEncryptionKey); + } CFReleaseNull(_circle); CFReleaseNull(_fullPeerInfo); } diff --git a/KeychainEntitledTestApp_ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/KeychainEntitledTestApp_ios/Assets.xcassets/AppIcon.appiconset/Contents.json index 1d060ed2..d8db8d65 100644 --- a/KeychainEntitledTestApp_ios/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/KeychainEntitledTestApp_ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -84,6 +84,11 @@ "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+SendMessage.m b/KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+SendMessage.m index 3136a7e7..168f2518 100644 --- a/KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+SendMessage.m +++ b/KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+SendMessage.m @@ -39,6 +39,7 @@ #import #include +#include #include #include @@ -129,6 +130,7 @@ static const NSUInteger kMaxIDSMessagePayloadSize = 64000; - (void) sendMessageToKVS: (NSDictionary*) encapsulatedKeychainMessage { + SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.kvsreroute"), 1); [encapsulatedKeychainMessage enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { if ([key isKindOfClass: [NSString class]] && [obj isKindOfClass:[NSData class]]) { [self sendToKVS:key message:obj]; @@ -442,6 +444,7 @@ static const NSUInteger kMaxIDSMessagePayloadSize = 64000; if([encapsulatedKeychainMessage isKindOfClass:[NSDictionary class]]){ secnotice("IDS Transport", "Encapsulated message: %@", encapsulatedKeychainMessage); [self sendMessageToKVS:encapsulatedKeychainMessage]; + } } diff --git a/OSX/authd/authitems.c b/OSX/authd/authitems.c index 50dc0a45..0468eaf7 100644 --- a/OSX/authd/authitems.c +++ b/OSX/authd/authitems.c @@ -649,6 +649,9 @@ void auth_items_copy(auth_items_t items, auth_items_t src) { auth_items_iterate(src, ^bool(const char *key) { + if (!key) { + return true; + } CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull); auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup); CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item); diff --git a/OSX/authd/debugging.h b/OSX/authd/debugging.h index 0e0638b3..b1dca37f 100644 --- a/OSX/authd/debugging.h +++ b/OSX/authd/debugging.h @@ -24,7 +24,12 @@ return log; \ #define CFReleaseNull(CF) { CFTypeRef _cf = (CF); \ if (_cf) { (CF) = NULL; CFRelease(_cf); } } #define CFRetainSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRetain(_cf); } - +#define CFAssignRetained(VAR,CF) ({ \ +__typeof__(VAR) *const _pvar = &(VAR); \ +__typeof__(CF) _cf = (CF); \ +(*_pvar) = *_pvar ? (CFRelease(*_pvar), _cf) : _cf; \ +}) + #define xpc_release_safe(obj) if (obj) { xpc_release(obj); obj = NULL; } #define free_safe(obj) if (obj) { free(obj); obj = NULL; } diff --git a/OSX/authd/engine.c b/OSX/authd/engine.c index 61bc15ee..08780ce1 100644 --- a/OSX/authd/engine.c +++ b/OSX/authd/engine.c @@ -21,11 +21,14 @@ int checkpw_internal( const struct passwd *pw, const char* password ); #include #include +#include #include #include #include #include #include +#include + AUTHD_DEFINE_LOG @@ -100,6 +103,7 @@ _engine_finalizer(CFTypeRef value) CFReleaseNull(engine->credentials); CFReleaseNull(engine->effectiveCredentials); CFReleaseNull(engine->authenticateRule); + CFReleaseNull(engine->la_context); } AUTH_TYPE_INSTANCE(engine, @@ -417,17 +421,19 @@ _evaluate_builtin_mechanism(engine_t engine, mechanism_t mech) static bool -_extract_password_from_la(engine_t engine, CFTypeRef la_context) +_extract_password_from_la(engine_t engine) { bool retval = false; + + if (!engine->la_context) { + return retval; + } + // try to retrieve secret - CFDataRef passdata = LACopyCredential(la_context, kLACredentialTypeExtractablePasscode, NULL); + CFDataRef passdata = LACopyCredential(engine->la_context, kLACredentialTypeExtractablePasscode, NULL); if (passdata) { if (CFDataGetBytePtr(passdata)) { auth_items_set_data(engine->context, kAuthorizationEnvironmentPassword, CFDataGetBytePtr(passdata), CFDataGetLength(passdata)); - } else { - char nulChar = 0; - auth_items_set_data(engine->context, kAuthorizationEnvironmentPassword, &nulChar, 1); } CFRelease(passdata); } @@ -449,53 +455,63 @@ _evaluate_mechanisms(engine_t engine, CFArrayRef mechanisms) CFDictionaryRef la_result = NULL; CFIndex count = CFArrayGetCount(mechanisms); - for (CFIndex i = 0; i < count; i++) { + bool sheet_evaluation = false; + if (engine->la_context) { + int tmp = kLAOptionNotInteractive; + CFNumberRef key = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp); + tmp = 1; + CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp); + if (key && value) { + CFMutableDictionaryRef options = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(options, key, value); + la_result = LACopyResultOfPolicyEvaluation(engine->la_context, kLAPolicyDeviceOwnerAuthentication, options, NULL); + CFReleaseSafe(options); + } + CFReleaseSafe(key); + CFReleaseSafe(value); + } + + for (CFIndex i = 0; i < count; i++) { mechanism_t mech = (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i); if (mechanism_get_type(mech)) { os_log_debug(AUTHD_LOG, "engine: running builtin mechanism %{public}s (%li of %li)", mechanism_get_string(mech), i+1, count); result = _evaluate_builtin_mechanism(engine, mech); } else { - bool sheet_variant_used = false; + bool shoud_run_agent = true; // evaluate comes from sheet -> we may not want to run standard SecurityAgent or authhost if (engine->la_context) { - - if (!la_result) { - int tmp = kLAOptionNotInteractive; - CFNumberRef key = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp); - tmp = 1; - CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp); - if (key && value) { - CFMutableDictionaryRef options = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionarySetValue(options, key, value); - la_result = LACopyResultOfPolicyEvaluation(engine->la_context, kLAPolicyDeviceOwnerAuthentication, options, NULL); - CFReleaseSafe(options); - } - CFReleaseSafe(key); - CFReleaseSafe(value); - } - // sheet variant in progress if (strcmp(mechanism_get_string(mech), "builtin:authenticate") == 0) { - // instead of running SecurityAgent, get uid from the authorization - os_log(AUTHD_LOG, "engine: running builtin sheet authenticate"); - if (!la_result) { - result = kAuthorizationResultDeny; // no la_result => was evaluate did not pass + // find out if sheet just provided credentials or did real authentication + // if password is provided or PAM service name exists, it means authd has to evaluate credentials + // otherwise we need to check la_result + if (auth_items_exist(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) || auth_items_exist(engine->context, kAuthorizationEnvironmentPassword)) { + // do not try to get credentials as it has been already passed by sheet + os_log(AUTHD_LOG, "engine: ingoring builtin sheet authenticate"); + } else { + // sheet itself did the authenticate the user + os_log(AUTHD_LOG, "engine: running builtin sheet authenticate"); + sheet_evaluation = true; + if (!la_result || TKGetSmartcardSetting(kTKEnforceSmartcard) != 0) { + result = kAuthorizationResultDeny; // no la_result => evaluate did not pass for sheet method. Enforced smartcard => no way to use sheet based evaluation + } } - sheet_variant_used = true; + shoud_run_agent = false; // SecurityAgent should not be run for builtin:authenticate } else if (strcmp(mechanism_get_string(mech), "builtin:authenticate,privileged") == 0) { - os_log(AUTHD_LOG, "engine: running builtin sheet privileged authenticate"); - if (!la_result) { - result = kAuthorizationResultDeny; // no la_result => was evaluate did not pass - } else { - if (!_extract_password_from_la(engine, engine->la_context)) { - os_log_debug(AUTHD_LOG, "engine: cannot extract cred"); + if (sheet_evaluation) { + os_log(AUTHD_LOG, "engine: running builtin sheet privileged authenticate"); + shoud_run_agent = false; + if (!la_result || TKGetSmartcardSetting(kTKEnforceSmartcard) != 0) { // should not get here under normal circumstances but we need to handle this case as well + result = kAuthorizationResultDeny; // no la_result => evaluate did not pass. Enforced smartcard => no way to use sheet based evaluation } + } else { + // should_run_agent has to be set to true because we want authorizationhost to verify the credentials + os_log(AUTHD_LOG, "engine: running sheet privileged authenticate"); } - sheet_variant_used = true; } } - if (!sheet_variant_used) { + if (shoud_run_agent) { agent_t agent = _get_agent(engine, mech, true, i == 0); require_action(agent != NULL, done, result = kAuthorizationResultUndefined; os_log_error(AUTHD_LOG, "engine: error creating mechanism agent")); @@ -632,6 +648,12 @@ _evaluate_authentication(engine_t engine, rule_t rule) require_action(CFArrayGetCount(mechanisms) > 0, done, os_log_debug(AUTHD_LOG, "engine: error no mechanisms found")); int64_t ruleTries = rule_get_tries(rule); + + if (engine->la_context) { + ruleTries = 1; + os_log_debug(AUTHD_LOG, "Sheet authentication in progress, one try is enough"); + } + for (engine->tries = 0; engine->tries < ruleTries; engine->tries++) { auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason)); @@ -697,11 +719,6 @@ _evaluate_authentication(engine_t engine, rule_t rule) } else if (status == errAuthorizationDenied) { os_log_error(AUTHD_LOG, "engine: evaluate denied"); engine->reason = invalidPassphrase; - if (engine->la_context) { - // for sheet authorizations do not retry with sheet as there is no new sheet UI - CFReleaseNull(engine->la_context); - auth_items_remove(engine->context, AGENT_CONTEXT_UID); - } } } @@ -709,8 +726,6 @@ _evaluate_authentication(engine_t engine, rule_t rule) engine->reason = tooManyTries; auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason)); auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries); - // TODO: determine why evaluate_mechanism is run once again and possibly remove this call - _evaluate_mechanisms(engine, mechanisms); ccaudit_log(ccaudit, engine->currentRightName, NULL, 1113); } @@ -1132,7 +1147,7 @@ static void _parse_environment(engine_t engine, auth_items_t environment) #endif // Check if a credential was passed into the environment and we were asked to extend the rights - if (engine->flags & kAuthorizationFlagExtendRights) { + if (engine->flags & kAuthorizationFlagExtendRights && !(engine->flags & kAuthorizationFlagSheet)) { const char * user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername); const char * pass = auth_items_get_string(environment, kAuthorizationEnvironmentPassword); const bool password_was_used = auth_items_get_string(environment, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) == nil; // AGENT_CONTEXT_AP_PAM_SERVICE_NAME in the context means alternative PAM was used @@ -1205,11 +1220,8 @@ OSStatus engine_preauthorize(engine_t engine, auth_items_t credentials) engine->flags = kAuthorizationFlagExtendRights; engine->preauthorizing = true; - CFTypeRef la_context = engine_copy_context(engine, credentials); - if (la_context) { - _extract_password_from_la(engine, la_context); - CFRelease(la_context); - } + CFAssignRetained(engine->la_context, engine_copy_context(engine, credentials)); + _extract_password_from_la(engine); const char *user = auth_items_get_string(credentials, kAuthorizationEnvironmentUsername); require(user, done); @@ -1338,15 +1350,73 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en if (auth_rights_get_count(rights) > 0) { ccaudit_log(ccaudit, "begin evaluation", NULL, 0); } - + + if (!auth_token_apple_signed(engine->auth)) { +#ifdef NDEBUG + flags &= ~kAuthorizationFlagIgnorePasswordOnly; + flags &= ~kAuthorizationFlagSheet; +#else + os_log_debug(AUTHD_LOG, "engine: in release mode, extra flags would be ommited as creator is not signed by Apple"); +#endif + } + engine->flags = flags; if (environment) { _parse_environment(engine, environment); auth_items_copy(engine->hints, environment); - engine_acquire_sheet_data(engine); } + if (engine->flags & kAuthorizationFlagSheet) { + CFTypeRef extract_password_entitlement = auth_token_copy_entitlement_value(engine->auth, "com.apple.authorization.extract-password"); + if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) { + save_password = true; + os_log_debug(AUTHD_LOG, "engine: authorization allowed to extract password"); + } else { + os_log_debug(AUTHD_LOG, "engine: authorization NOT allowed to extract password"); + } + CFReleaseSafe(extract_password_entitlement); + + // TODO: Remove when all clients have adopted entitlement + if (!enforced_entitlement()) { + save_password = true; + } + const char *user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername); + require(user, done); + + auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user); + struct passwd *pwd = getpwnam(user); + require(pwd, done); + auth_items_set_int(engine->context, AGENT_CONTEXT_UID, pwd->pw_uid); + + // move sheet-specific items from hints to context + const char *service = auth_items_get_string(engine->hints, AGENT_CONTEXT_AP_PAM_SERVICE_NAME); + if (service) { + if (auth_items_exist(engine->hints, AGENT_CONTEXT_AP_USER_NAME)) { + auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, auth_items_get_string(engine->hints, AGENT_CONTEXT_AP_USER_NAME)); + auth_items_remove(engine->hints, AGENT_CONTEXT_AP_USER_NAME); + } else { + auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, user); + } + + auth_items_set_string(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME, service); + auth_items_remove(engine->hints, AGENT_CONTEXT_AP_PAM_SERVICE_NAME); + } + + if (auth_items_exist(environment, AGENT_CONTEXT_AP_TOKEN)) { + size_t datalen = 0; + const void *data = auth_items_get_data(engine->hints, AGENT_CONTEXT_AP_TOKEN, &datalen); + if (data) { + auth_items_set_data(engine->context, AGENT_CONTEXT_AP_TOKEN, data, datalen); + } + auth_items_remove(engine->hints, AGENT_CONTEXT_AP_TOKEN); + } + + engine_acquire_sheet_data(engine); + _extract_password_from_la(engine); + engine->preauthorizing = true; + } + auth_items_t decrypted_items = auth_items_create(); require_action(decrypted_items != NULL, done, os_log_error(AUTHD_LOG, "engine: enable to create items")); auth_items_content_copy(decrypted_items, auth_token_get_context(engine->auth)); @@ -1357,6 +1427,7 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en engine->dismissed = false; auth_rights_clear(engine->grantedRights); + if (!(engine->flags & kAuthorizationFlagIgnorePasswordOnly)) { // first check if any of rights uses rule with password-only set to true // if so, set appropriate hint so SecurityAgent won't use alternate authentication methods like smartcard etc. @@ -1377,6 +1448,8 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en return true; }); authdb_connection_release(&dbconn); // release db handle + } else { + os_log_info(AUTHD_LOG, "engine: password-only ignored"); } if (password_only) { @@ -1486,7 +1559,11 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en } os_log_debug(AUTHD_LOG, "engine: authorize result: %d", (int)status); - + + if (engine->flags & kAuthorizationFlagSheet) { + engine->preauthorizing = false; + } + if ((engine->flags & kAuthorizationFlagExtendRights) && !(engine->flags & kAuthorizationFlagDestroyRights)) { _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) { credential_t cred = (credential_t)value; @@ -1727,20 +1804,14 @@ CFTypeRef engine_copy_context(engine_t engine, auth_items_t source) bool engine_acquire_sheet_data(engine_t engine) { - uid_t uid = auth_items_get_int(engine->hints, AGENT_CONTEXT_UID); + uid_t uid = auth_items_get_int(engine->context, AGENT_CONTEXT_UID); if (!uid) return false; CFReleaseSafe(engine->la_context); engine->la_context = engine_copy_context(engine, engine->hints); if (engine->la_context) { - // copy UID to the context of the authorization os_log_debug(AUTHD_LOG, "engine: Sheet user UID %d", uid); - auth_items_set_int(engine->context, AGENT_CONTEXT_UID, uid); - struct passwd *pwd = getpwuid(uid); - if (pwd) { - auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, pwd->pw_name); - } return true; } else { // this is not real failure as no LA context in authorization context is very valid scenario diff --git a/OSX/config/base.xcconfig b/OSX/config/base.xcconfig index 0e70fa2a..56529e08 100644 --- a/OSX/config/base.xcconfig +++ b/OSX/config/base.xcconfig @@ -1,11 +1,10 @@ + SDKROOT = macosx.internal ARCHS[sdk=macosx*] = $(ARCHS_STANDARD_32_64_BIT) CODE_SIGN_IDENTITY = -; GCC_VERSION = com.apple.compilers.llvm.clang.1_0 DEBUG_INFORMATION_FORMAT = dwarf-with-dsym -CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion) -VERSIONING_SYSTEM = apple-generic; DEAD_CODE_STRIPPING = YES diff --git a/OSX/config/security_macos.xcconfig b/OSX/config/security_macos.xcconfig index 73f31cac..0a2b9ed2 100644 --- a/OSX/config/security_macos.xcconfig +++ b/OSX/config/security_macos.xcconfig @@ -1,4 +1,5 @@ #include "OSX/config/base.xcconfig" +#include "xcconfig/Version.xcconfig" GCC_PRECOMPILE_PREFIX_HEADER = YES @@ -14,7 +15,7 @@ GCC_C_LANGUAGE_STANDARD = gnu99 SUPPORTED_PLATFORMS = macOS // Don't use the inherited cflags; they set SEC_IOS_ON_OSX -GCC_PREPROCESSOR_DEFINITIONS = +GCC_PREPROCESSOR_DEFINITIONS = SECURITY_BUILD_VERSION=\"$(SECURITY_BUILD_VERSION)\" GCC_TREAT_WARNINGS_AS_ERRORS = YES GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO diff --git a/OSX/lib/en.lproj/authorization.prompts.strings b/OSX/lib/en.lproj/authorization.prompts.strings index 29b23439..17260277 100644 --- a/OSX/lib/en.lproj/authorization.prompts.strings +++ b/OSX/lib/en.lproj/authorization.prompts.strings @@ -156,7 +156,7 @@ "com.apple.ctkbind.admin" = "__APPNAME__ is trying to pair the current user with the SmartCard identity."; -"com.apple.builtin.sc-kc-new-passphrase" = "The system will now create a keychain to store your secrets. Your smart card will automatically unlock it. Please choose a password that can unlock it separately. You may use your account password or pick another one. For security reasons, do not use your smart card PIN or similar text."; +"com.apple.builtin.sc-kc-new-passphrase" = "The system will now create a keychain to store your secrets. Your SmartCard will automatically unlock it. Please choose a password that can unlock it separately. You may use your account password or pick another one. For security reasons, do not use your SmartCard PIN or similar text."; "com.apple.security.sudo" = "__APPNAME__ is trying to execute a command as administrator."; diff --git a/OSX/libsecurity_authorization/lib/AuthorizationPriv.h b/OSX/libsecurity_authorization/lib/AuthorizationPriv.h index 332740ba..e5389d6f 100644 --- a/OSX/libsecurity_authorization/lib/AuthorizationPriv.h +++ b/OSX/libsecurity_authorization/lib/AuthorizationPriv.h @@ -53,7 +53,9 @@ extern "C" { @enum Private (for now) AuthorizationFlags */ enum { - kAuthorizationFlagLeastPrivileged = (1 << 5) + kAuthorizationFlagLeastPrivileged = (1 << 5), + kAuthorizationFlagSheet = (1 << 6), + kAuthorizationFlagIgnorePasswordOnly = (1 << 7), }; /*! diff --git a/OSX/libsecurity_codesigning/lib/StaticCode.cpp b/OSX/libsecurity_codesigning/lib/StaticCode.cpp index 8ec4a437..ff621d53 100644 --- a/OSX/libsecurity_codesigning/lib/StaticCode.cpp +++ b/OSX/libsecurity_codesigning/lib/StaticCode.cpp @@ -883,7 +883,7 @@ bool SecStaticCode::verifySignature() } // Did we implicitly trust the signer? - mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified); + mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed); return false; // XXX: Not checking for expired certs #endif diff --git a/OSX/libsecurity_keychain/lib/CertificateValues.cpp b/OSX/libsecurity_keychain/lib/CertificateValues.cpp index 7fd526d1..66470a3f 100644 --- a/OSX/libsecurity_keychain/lib/CertificateValues.cpp +++ b/OSX/libsecurity_keychain/lib/CertificateValues.cpp @@ -1,15 +1,15 @@ /* - * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved. + * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,7 +17,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ @@ -31,17 +31,14 @@ #include #include "SecCertificateOIDs.h" #include "CertificateValues.h" -#include "SecCertificateP.h" -#include "SecCertificatePrivP.h" #include -#include "SecCertificateP.h" -/* FIXME including SecCertificateInternalP.h here produces errors; investigate */ -extern "C" CFDataRef SecCertificateCopyIssuerSequenceP(SecCertificateRefP certificate); -extern "C" CFDataRef SecCertificateCopySubjectSequenceP(SecCertificateRefP certificate); -extern "C" CFDictionaryRef SecCertificateCopyAttributeDictionaryP(SecCertificateRefP certificate); - -extern "C" void appendPropertyP(CFMutableArrayRef properties, CFStringRef propertyType, CFStringRef label, CFTypeRef value); +// SecCertificateInternal.h cannot be included in this file, due to its +// use of types which are not resolved in our macOS-only library. +// +extern "C" CFArrayRef SecCertificateCopyLegacyProperties(SecCertificateRef certificate); +extern "C" void appendProperty(CFMutableArrayRef properties, CFStringRef propertyType, + CFStringRef label, CFStringRef localizedLabel, CFTypeRef value); extern const CFStringRef __nonnull kSecPropertyKeyType; extern const CFStringRef __nonnull kSecPropertyKeyLabel; @@ -75,7 +72,8 @@ typedef struct FieldValueFilterContext } FieldValueFilterContext; CertificateValues::CertificateValues(SecCertificateRef certificateRef) : mCertificateRef(certificateRef), - mCertificateData(NULL) + mCertificateData(NULL), + mCertificateProperties(NULL) { if (mCertificateRef) CFRetain(mCertificateRef); @@ -83,12 +81,29 @@ CertificateValues::CertificateValues(SecCertificateRef certificateRef) : mCertif CertificateValues::~CertificateValues() throw() { + if (mCertificateProperties) + CFRelease(mCertificateProperties); if (mCertificateData) CFRelease(mCertificateData); if (mCertificateRef) CFRelease(mCertificateRef); } +CFArrayRef CertificateValues::copyPropertyValues(CFErrorRef *error) +{ + if (!mCertificateProperties) { + mCertificateProperties = SecCertificateCopyLegacyProperties(mCertificateRef); + } + if (mCertificateProperties) { + CFRetain(mCertificateProperties); + } + else if (error) { + *error = CFErrorCreate(NULL, + kCFErrorDomainOSStatus, errSecInvalidCertificateRef, NULL); + } + return mCertificateProperties; +} + CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef *error) { if (keys) @@ -119,8 +134,8 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef * } } - SecCertificateRefP certificateP = SecCertificateCreateWithDataP(kCFAllocatorDefault, mCertificateData); - if (!certificateP) + SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, mCertificateData); + if (!certificate) { if (error) *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL); @@ -131,93 +146,93 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef * &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // Return an array of CFStringRefs representing the common names in the certificates subject if any - CFArrayRef commonNames=SecCertificateCopyCommonNamesP(certificateP); + CFArrayRef commonNames=SecCertificateCopyCommonNames(certificate); if (commonNames) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("CN"), commonNames); + appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("CN"), NULL, commonNames); CFDictionaryAddValue(fieldValues, kSecOIDCommonName, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(commonNames); CFRelease(additionalValues); } // These can exist in the subject alt name or in the subject - CFArrayRef dnsNames=SecCertificateCopyDNSNamesP(certificateP); + CFArrayRef dnsNames=SecCertificateCopyDNSNames(certificate); if (dnsNames) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), dnsNames); + appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), NULL, dnsNames); CFDictionaryAddValue(fieldValues, CFSTR("DNSNAMES"), (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(dnsNames); CFRelease(additionalValues); } - CFArrayRef ipAddresses=SecCertificateCopyIPAddressesP(certificateP); + CFArrayRef ipAddresses=SecCertificateCopyIPAddresses(certificate); if (ipAddresses) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("IP"), dnsNames); + appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("IP"), NULL, dnsNames); CFDictionaryAddValue(fieldValues, CFSTR("IPADDRESSES"), (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(ipAddresses); CFRelease(additionalValues); } // These can exist in the subject alt name or in the subject - CFArrayRef emailAddrs=SecCertificateCopyRFC822NamesP(certificateP); + CFArrayRef emailAddrs=SecCertificateCopyRFC822Names(certificate); if (emailAddrs) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), dnsNames); + appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("DNS"), NULL, dnsNames); CFDictionaryAddValue(fieldValues, kSecOIDEmailAddress, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(emailAddrs); CFRelease(additionalValues); } - CFAbsoluteTime notBefore = SecCertificateNotValidBeforeP(certificateP); + CFAbsoluteTime notBefore = SecCertificateNotValidBefore(certificate); CFNumberRef notBeforeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, ¬Before); if (notBeforeRef) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid Before"), notBeforeRef); + appendProperty(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid Before"), NULL, notBeforeRef); CFDictionaryAddValue(fieldValues, kSecOIDX509V1ValidityNotBefore, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(notBeforeRef); CFRelease(additionalValues); } - CFAbsoluteTime notAfter = SecCertificateNotValidAfterP(certificateP); + CFAbsoluteTime notAfter = SecCertificateNotValidAfter(certificate); CFNumberRef notAfterRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, ¬After); if (notAfterRef) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid After"), notAfterRef); + appendProperty(additionalValues, kSecPropertyTypeNumber, CFSTR("Not Valid After"), NULL, notAfterRef); CFDictionaryAddValue(fieldValues, kSecOIDX509V1ValidityNotAfter, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(notAfterRef); CFRelease(additionalValues); } - SecKeyUsage keyUsage=SecCertificateGetKeyUsageP(certificateP); + SecKeyUsage keyUsage=SecCertificateGetKeyUsage(certificate); CFNumberRef ku = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keyUsage); if (ku) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeNumber, CFSTR("Key Usage"), ku); + appendProperty(additionalValues, kSecPropertyTypeNumber, CFSTR("Key Usage"), NULL, ku); CFDictionaryAddValue(fieldValues, kSecOIDKeyUsage, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(ku); CFRelease(additionalValues); } - CFArrayRef ekus = SecCertificateCopyExtendedKeyUsageP(certificateP); + CFArrayRef ekus = SecCertificateCopyExtendedKeyUsage(certificate); if (ekus) { CFMutableArrayRef additionalValues = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - appendPropertyP(additionalValues, kSecPropertyTypeArray, CFSTR("Extended Key Usage"), ekus); + appendProperty(additionalValues, kSecPropertyTypeArray, CFSTR("Extended Key Usage"), NULL, ekus); CFDictionaryAddValue(fieldValues, kSecOIDExtendedKeyUsage, (CFTypeRef)CFArrayGetValueAtIndex(additionalValues, 0)); CFRelease(ekus); CFRelease(additionalValues); } // Add all values from properties dictionary - CFArrayRef properties = SecCertificateCopyPropertiesP(certificateP); + CFArrayRef properties = copyPropertyValues(NULL); if (properties) { CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)properties)); @@ -228,7 +243,7 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef * CFAbsoluteTime verifyTime = CFAbsoluteTimeGetCurrent(); CFMutableArrayRef summaryProperties = - SecCertificateCopySummaryPropertiesP(certificateP, verifyTime); + SecCertificateCopySummaryProperties(certificate, verifyTime); if (summaryProperties) { CFRange range = CFRangeMake(0, CFArrayGetCount((CFArrayRef)summaryProperties)); @@ -238,8 +253,8 @@ CFDictionaryRef CertificateValues::copyFieldValues(CFArrayRef keys, CFErrorRef * CFRelease(summaryProperties); } - if (certificateP) - CFRelease(certificateP); + if (certificate) + CFRelease(certificate); if (keys==NULL) return (CFDictionaryRef)fieldValues; @@ -362,12 +377,12 @@ CFStringRef CertificateValues::remapLabelToKey(CFStringRef label) CFDataRef CertificateValues::copySerialNumber(CFErrorRef *error) { CFDataRef result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); + SecCertificateRef certificate = copySecCertificateRef(error); - if (certificateP) + if (certificate) { - result = SecCertificateCopySerialNumberP(certificateP); - CFRelease(certificateP); + result = SecCertificateCopySerialNumberData(certificate, error); + CFRelease(certificate); } return result; } @@ -375,11 +390,14 @@ CFDataRef CertificateValues::copySerialNumber(CFErrorRef *error) CFDataRef CertificateValues::copyNormalizedIssuerContent(CFErrorRef *error) { CFDataRef result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateCopyNormalizedIssuerSequenceP(certificateP); - CFRelease(certificateP); + // this matches the behavior on OS X prior to 10.12, where + // normalized content was actually returned as a sequence. + + result = SecCertificateCopyNormalizedIssuerSequence(certificate); + CFRelease(certificate); } return result; } @@ -387,11 +405,14 @@ CFDataRef CertificateValues::copyNormalizedIssuerContent(CFErrorRef *error) CFDataRef CertificateValues::copyNormalizedSubjectContent(CFErrorRef *error) { CFDataRef result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateCopyNormalizedSubjectSequenceP(certificateP); - CFRelease(certificateP); + // this matches the behavior on OS X prior to 10.12, where + // normalized content was actually returned as a sequence. + + result = SecCertificateCopyNormalizedSubjectSequence(certificate); + CFRelease(certificate); } return result; } @@ -399,11 +420,11 @@ CFDataRef CertificateValues::copyNormalizedSubjectContent(CFErrorRef *error) CFDataRef CertificateValues::copyIssuerSequence(CFErrorRef *error) { CFDataRef result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateCopyIssuerSequenceP(certificateP); - CFRelease(certificateP); + result = SecCertificateCopyIssuerSequence(certificate); + CFRelease(certificate); } return result; } @@ -411,35 +432,59 @@ CFDataRef CertificateValues::copyIssuerSequence(CFErrorRef *error) CFDataRef CertificateValues::copySubjectSequence(CFErrorRef *error) { CFDataRef result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) + { + result = SecCertificateCopySubjectSequence(certificate); + CFRelease(certificate); + } + return result; +} + +CFStringRef CertificateValues::copyIssuerSummary(CFErrorRef *error) +{ + CFStringRef result = NULL; + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateCopySubjectSequenceP(certificateP); - CFRelease(certificateP); + result = SecCertificateCopyIssuerSummary(certificate); + CFRelease(certificate); + } + return result; +} + +CFStringRef CertificateValues::copySubjectSummary(CFErrorRef *error) +{ + CFStringRef result = NULL; + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) + { + result = SecCertificateCopySubjectSummary(certificate); + CFRelease(certificate); } return result; } CFDictionaryRef CertificateValues::copyAttributeDictionary(CFErrorRef *error) { - CFDictionaryRef result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) - { - result = SecCertificateCopyAttributeDictionaryP(certificateP); - CFRelease(certificateP); - } - return result; + CFDictionaryRef result = NULL; + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) + { + result = SecCertificateCopyAttributeDictionary(certificate); + CFRelease(certificate); + } + return result; } bool CertificateValues::isValid(CFAbsoluteTime verifyTime, CFErrorRef *error) { bool result = NULL; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateIsValidP(certificateP, verifyTime); - CFRelease(certificateP); + result = SecCertificateIsValid(certificate, verifyTime); + CFRelease(certificate); } return result; } @@ -447,11 +492,11 @@ bool CertificateValues::isValid(CFAbsoluteTime verifyTime, CFErrorRef *error) CFAbsoluteTime CertificateValues::notValidBefore(CFErrorRef *error) { CFAbsoluteTime result = 0; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateNotValidBeforeP(certificateP); - CFRelease(certificateP); + result = SecCertificateNotValidBefore(certificate); + CFRelease(certificate); } return result; } @@ -459,16 +504,16 @@ CFAbsoluteTime CertificateValues::notValidBefore(CFErrorRef *error) CFAbsoluteTime CertificateValues::notValidAfter(CFErrorRef *error) { CFAbsoluteTime result = 0; - SecCertificateRefP certificateP = getSecCertificateRefP(error); - if (certificateP) + SecCertificateRef certificate = copySecCertificateRef(error); + if (certificate) { - result = SecCertificateNotValidAfterP(certificateP); - CFRelease(certificateP); + result = SecCertificateNotValidAfter(certificate); + CFRelease(certificate); } return result; } -SecCertificateRefP CertificateValues::getSecCertificateRefP(CFErrorRef *error) +SecCertificateRef CertificateValues::copySecCertificateRef(CFErrorRef *error) { // SecCertificateCopyData returns an object created with CFDataCreate, so we // own it and must release it @@ -480,20 +525,26 @@ SecCertificateRefP CertificateValues::getSecCertificateRefP(CFErrorRef *error) } mCertificateData = SecCertificateCopyData(mCertificateRef); // OK to call, no big lock - if (!mCertificateData && error) + if (!mCertificateData) { - *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateRef, NULL); + if (error) + { + *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateRef, NULL); + } return NULL; } - SecCertificateRefP certificateP = SecCertificateCreateWithDataP(kCFAllocatorDefault, mCertificateData); - if (!certificateP && error) + SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, mCertificateData); + if (!certificate) { - *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL); + if (error) + { + *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificateGroup, NULL); + } return NULL; } - return certificateP; + return certificate; } #pragma mark ---------- OID Constants ---------- diff --git a/OSX/libsecurity_keychain/lib/CertificateValues.h b/OSX/libsecurity_keychain/lib/CertificateValues.h index 37de693f..41d17c3f 100644 --- a/OSX/libsecurity_keychain/lib/CertificateValues.h +++ b/OSX/libsecurity_keychain/lib/CertificateValues.h @@ -1,15 +1,15 @@ /* - * Copyright (c) 2002-2014 Apple Inc. All Rights Reserved. + * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,7 +17,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ @@ -27,9 +27,8 @@ #ifndef _SECURITY_CERTIFICATEVALUES_H_ #define _SECURITY_CERTIFICATEVALUES_H_ -#include -#include "SecBaseP.h" -//#include +#include + namespace Security { @@ -37,33 +36,37 @@ namespace Security namespace KeychainCore { -class CertificateValues// : public SecCFObject +class CertificateValues { NOCOPY(CertificateValues) public: CertificateValues(SecCertificateRef certificateRef); - virtual ~CertificateValues() throw(); + virtual ~CertificateValues() throw(); static CFStringRef remapLabelToKey(CFStringRef label); + CFArrayRef copyPropertyValues(CFErrorRef *error); CFDictionaryRef copyFieldValues(CFArrayRef keys, CFErrorRef *error); CFDataRef copySerialNumber(CFErrorRef *error); CFDataRef copyNormalizedIssuerContent(CFErrorRef *error); CFDataRef copyNormalizedSubjectContent(CFErrorRef *error); CFDataRef copyIssuerSequence(CFErrorRef *error); CFDataRef copySubjectSequence(CFErrorRef *error); - CFDictionaryRef copyAttributeDictionary(CFErrorRef *error); + CFStringRef copyIssuerSummary(CFErrorRef *error); + CFStringRef copySubjectSummary(CFErrorRef *error); + CFDictionaryRef copyAttributeDictionary(CFErrorRef *error); bool isValid(CFAbsoluteTime verifyTime, CFErrorRef *error); CFAbsoluteTime notValidBefore(CFErrorRef *error); CFAbsoluteTime notValidAfter(CFErrorRef *error); private: - SecCertificateRefP getSecCertificateRefP(CFErrorRef *error); + SecCertificateRef copySecCertificateRef(CFErrorRef *error); SecCertificateRef mCertificateRef; CFDataRef mCertificateData; + CFArrayRef mCertificateProperties; static CFDictionaryRef mOIDRemap; }; diff --git a/OSX/libsecurity_keychain/lib/SecBase64P.c b/OSX/libsecurity_keychain/lib/SecBase64P.c deleted file mode 100644 index c043906c..00000000 --- a/OSX/libsecurity_keychain/lib/SecBase64P.c +++ /dev/null @@ -1,489 +0,0 @@ -/* ///////////////////////////////////////////////////////////////////////////// - * File: b64.c - * - * Purpose: Implementation file for the b64 library - * - * Created: 18th October 2004 - * Updated: 2nd August 2006 - * - * Home: http://synesis.com.au/software/ - * - * Copyright (c) 2004-2006, Matthew Wilson and Synesis Software - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of - * any contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * ////////////////////////////////////////////////////////////////////////// */ - - -/** \file b64.c Implementation file for the b64 library - */ - -#include "SecBase64P.h" - -#include -#include - -/* ///////////////////////////////////////////////////////////////////////////// - * Constants and definitions - */ - -#ifndef B64_DOCUMENTATION_SKIP_SECTION -# define NUM_PLAIN_DATA_BYTES (3) -# define NUM_ENCODED_DATA_BYTES (4) -#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ - -/* ///////////////////////////////////////////////////////////////////////////// - * Warnings - */ - -#if defined(_MSC_VER) && \ - _MSC_VER < 1000 -# pragma warning(disable : 4705) -#endif /* _MSC_VER < 1000 */ - -/* ///////////////////////////////////////////////////////////////////////////// - * Data - */ - -static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static const signed char b64_indexes[] = -{ - /* 0 - 31 / 0x00 - 0x1f */ - -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - /* 32 - 63 / 0x20 - 0x3f */ - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, 62, -1, -1, -1, 63 /* ... , '+', ... '/' */ - , 52, 53, 54, 55, 56, 57, 58, 59 /* '0' - '7' */ - , 60, 61, -1, -1, -1, -1, -1, -1 /* '8', '9', ... */ - /* 64 - 95 / 0x40 - 0x5f */ - , -1, 0, 1, 2, 3, 4, 5, 6 /* ..., 'A' - 'G' */ - , 7, 8, 9, 10, 11, 12, 13, 14 /* 'H' - 'O' */ - , 15, 16, 17, 18, 19, 20, 21, 22 /* 'P' - 'W' */ - , 23, 24, 25, -1, -1, -1, -1, -1 /* 'X', 'Y', 'Z', ... */ - /* 96 - 127 / 0x60 - 0x7f */ - , -1, 26, 27, 28, 29, 30, 31, 32 /* ..., 'a' - 'g' */ - , 33, 34, 35, 36, 37, 38, 39, 40 /* 'h' - 'o' */ - , 41, 42, 43, 44, 45, 46, 47, 48 /* 'p' - 'w' */ - , 49, 50, 51, -1, -1, -1, -1, -1 /* 'x', 'y', 'z', ... */ - - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 - , -1, -1, -1, -1, -1, -1, -1, -1 -}; - -/* ///////////////////////////////////////////////////////////////////////////// - * Helper functions - */ - -/** This function reads in 3 bytes at a time, and translates them into 4 - * characters. - */ -static size_t SecBase64Encode_( unsigned char const *src - , size_t srcSize - , char *const dest - , size_t destLen - , unsigned lineLen - , SecBase64Result *rc) -{ - size_t total = ((srcSize + (NUM_PLAIN_DATA_BYTES - 1)) / NUM_PLAIN_DATA_BYTES) * NUM_ENCODED_DATA_BYTES; - - assert(NULL != rc); - *rc = kSecB64_R_OK; - - if(lineLen > 0) - { - size_t numLines = (total + (lineLen - 1)) / lineLen; - - total += 2 * (numLines - 1); - } - - if(NULL == dest) - { - return total; - } - else if(destLen < total) - { - *rc = kSecB64_R_INSUFFICIENT_BUFFER; - - return 0; - } - else - { - char *p = dest; - char *end = dest + destLen; - size_t len = 0; - - for(; NUM_PLAIN_DATA_BYTES <= srcSize; srcSize -= NUM_PLAIN_DATA_BYTES) - { - char characters[NUM_ENCODED_DATA_BYTES]; - - /* - * - * | 0 | 1 | 2 | - * - * | | | | - * | | | | | | | - * | | | | | | | | | | | | | - * | | | | | | | | | | | | | | | | | | | | | | | | | - * - * | 0 | 1 | 2 | 3 | - * - */ - - /* characters[0] is the 6 left-most bits of src[0] */ - characters[0] = (char)((src[0] & 0xfc) >> 2); - /* characters[0] is the right-most 2 bits of src[0] and the left-most 4 bits of src[1] */ - characters[1] = (char)(((src[0] & 0x03) << 4) + ((src[1] & 0xf0) >> 4)); - /* characters[0] is the right-most 4 bits of src[1] and the 2 left-most bits of src[2] */ - characters[2] = (char)(((src[1] & 0x0f) << 2) + ((src[2] & 0xc0) >> 6)); - /* characters[3] is the right-most 6 bits of src[2] */ - characters[3] = (char)(src[2] & 0x3f); - -#ifndef __WATCOMC__ - assert(characters[0] >= 0 && characters[0] < 64); - assert(characters[1] >= 0 && characters[1] < 64); - assert(characters[2] >= 0 && characters[2] < 64); - assert(characters[3] >= 0 && characters[3] < 64); -#endif /* __WATCOMC__ */ - - src += NUM_PLAIN_DATA_BYTES; - *p++ = b64_chars[(unsigned char)characters[0]]; - assert(NULL != strchr(b64_chars, *(p-1))); - ++len; - assert(len != lineLen); - - *p++ = b64_chars[(unsigned char)characters[1]]; - assert(NULL != strchr(b64_chars, *(p-1))); - ++len; - assert(len != lineLen); - - *p++ = b64_chars[(unsigned char)characters[2]]; - assert(NULL != strchr(b64_chars, *(p-1))); - ++len; - assert(len != lineLen); - - *p++ = b64_chars[(unsigned char)characters[3]]; - assert(NULL != strchr(b64_chars, *(p-1))); - - if( ++len == lineLen && - p != end) - { - *p++ = '\r'; - *p++ = '\n'; - len = 0; - } - } - - if(0 != srcSize) - { - /* Deal with the overspill, by boosting it up to three bytes (using 0s) - * and then appending '=' for any missing characters. - * - * This is done into a temporary buffer, so we can call ourselves and - * have the output continue to be written direct to the destination. - */ - - unsigned char dummy[NUM_PLAIN_DATA_BYTES]; - size_t i; - - for(i = 0; i < srcSize; ++i) - { - dummy[i] = *src++; - } - - for(; i < NUM_PLAIN_DATA_BYTES; ++i) - { - dummy[i] = '\0'; - } - - SecBase64Encode_(&dummy[0], NUM_PLAIN_DATA_BYTES, p, NUM_ENCODED_DATA_BYTES * (1 + 2), 0, rc); - - for(p += 1 + srcSize; srcSize++ < NUM_PLAIN_DATA_BYTES; ) - { - *p++ = '='; - } - } - - return total; - } -} - -/** This function reads in a character string in 4-character chunks, and writes - * out the converted form in 3-byte chunks to the destination. - */ -static size_t SecBase64Decode_( char const *src - , size_t srcLen - , unsigned char *dest - , size_t destSize - , unsigned flags - , char const **badChar - , SecBase64Result *rc) -{ - const size_t wholeChunks = (srcLen / NUM_ENCODED_DATA_BYTES); - const size_t remainderBytes = (srcLen % NUM_ENCODED_DATA_BYTES); - size_t maxTotal = (wholeChunks + (0 != remainderBytes)) * NUM_PLAIN_DATA_BYTES; - unsigned char *dest_ = dest; - - ((void)remainderBytes); - - assert(NULL != badChar); - assert(NULL != rc); - - *badChar = NULL; - *rc = kSecB64_R_OK; - - if(NULL == dest) - { - return maxTotal; - } - else if(destSize < maxTotal) - { - *rc = kSecB64_R_INSUFFICIENT_BUFFER; - - return 0; - } - else - { - /* Now we iterate through the src, collecting together four characters - * at a time from the Base-64 alphabet, until the end-point is reached. - * - * - */ - - char const *begin = src; - char const *const end = begin + srcLen; - size_t currIndex = 0; - size_t numPads = 0; - signed char indexes[NUM_ENCODED_DATA_BYTES]; /* 4 */ - - for(; begin != end; ++begin) - { - const char ch = *begin; - - if('=' == ch) - { - assert(currIndex < NUM_ENCODED_DATA_BYTES); - - indexes[currIndex++] = '\0'; - - ++numPads; - } - else - { - signed char ix = b64_indexes[(unsigned char)ch]; - - if(-1 == ix) - { - switch(ch) - { - case ' ': - case '\t': - case '\b': - case '\v': - if(kSecB64_F_STOP_ON_UNEXPECTED_WS & flags) - { - *rc = kSecB64_R_DATA_ERROR; - *badChar = begin; - return 0; - } - else - { - /* Fall through */ - } - case '\r': - case '\n': - continue; - default: - if(kSecB64_F_STOP_ON_UNKNOWN_CHAR & flags) - { - *rc = kSecB64_R_DATA_ERROR; - *badChar = begin; - return 0; - } - else - { - continue; - } - } - } - else - { - numPads = 0; - - assert(currIndex < NUM_ENCODED_DATA_BYTES); - - indexes[currIndex++] = ix; - } - } - - if(NUM_ENCODED_DATA_BYTES == currIndex) - { - unsigned char bytes[NUM_PLAIN_DATA_BYTES]; /* 3 */ - - bytes[0] = (unsigned char)((indexes[0] << 2) + ((indexes[1] & 0x30) >> 4)); - - currIndex = 0; - - *dest++ = bytes[0]; - if(2 != numPads) - { - bytes[1] = (unsigned char)(((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2)); - - *dest++ = bytes[1]; - - if(1 != numPads) - { - bytes[2] = (unsigned char)(((indexes[2] & 0x3) << 6) + indexes[3]); - - *dest++ = bytes[2]; - } - } - if(0 != numPads) - { - break; - } - } - } - - return (size_t)(dest - dest_); - } -} - -/* ///////////////////////////////////////////////////////////////////////////// - * API functions - */ - -size_t SecBase64Encode(void const *src, size_t srcSize, char *dest, size_t destLen) -{ - /* Use Null Object (Variable) here for rc, so do not need to check - * elsewhere. - */ - SecBase64Result rc_; - - return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, 0, &rc_); -} - -size_t SecBase64Encode2( void const *src - , size_t srcSize - , char *dest - , size_t destLen - , unsigned flags - , int lineLen /* = -1 */ - , SecBase64Result *rc /* = NULL */) -{ - /* Use Null Object (Variable) here for rc, so do not need to check - * elsewhere - */ - SecBase64Result rc_; - if(NULL == rc) - { - rc = &rc_; - } - - switch(kSecB64_F_LINE_LEN_MASK & flags) - { - case kSecB64_F_LINE_LEN_USE_PARAM: - if(lineLen >= 0) - { - break; - } - /* Fall through to 64 */ - case kSecB64_F_LINE_LEN_64: - lineLen = 64; - break; - case kSecB64_F_LINE_LEN_76: - lineLen = 76; - break; - default: - assert(!"Bad line length flag specified to SecBase64Encode2()"); - case kSecB64_F_LINE_LEN_INFINITE: - lineLen = 0; - break; - } - - assert(0 == (lineLen % 4)); - - return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, (unsigned)lineLen, rc); -} - -size_t SecBase64Decode(char const *src, size_t srcLen, void *dest, size_t destSize) -{ - /* Use Null Object (Variable) here for rc and badChar, so do not need to - * check elsewhere. - */ - char const *badChar_; - SecBase64Result rc_; - - return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, kSecB64_F_STOP_ON_NOTHING, &badChar_, &rc_); -} - -size_t SecBase64Decode2( char const *src - , size_t srcLen - , void *dest - , size_t destSize - , unsigned flags - , char const **badChar /* = NULL */ - , SecBase64Result *rc /* = NULL */) -{ - char const *badChar_; - SecBase64Result rc_; - - /* Use Null Object (Variable) here for rc and badChar, so do not need to - * check elsewhere. - */ - if(NULL == badChar) - { - badChar = &badChar_; - } - if(NULL == rc) - { - rc = &rc_; - } - - return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, flags, badChar, rc); -} - -/* ////////////////////////////////////////////////////////////////////////// */ diff --git a/OSX/libsecurity_keychain/lib/SecCertificate.cpp b/OSX/libsecurity_keychain/lib/SecCertificate.cpp index a9b145d4..ea044d6c 100644 --- a/OSX/libsecurity_keychain/lib/SecCertificate.cpp +++ b/OSX/libsecurity_keychain/lib/SecCertificate.cpp @@ -45,8 +45,6 @@ #include #include #include "CertificateValues.h" -#include "SecCertificateP.h" -#include "SecCertificatePrivP.h" #include "AppleBaselineEscrowCertificates.h" @@ -992,3 +990,20 @@ bool SecCertificateIsValidX(SecCertificateRef certificate, CFAbsoluteTime verify */ return SecCertificateIsValid(certificate, verifyTime); } + +/* OS X only */ +CFDataRef SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator, + CFDataRef der_certificate) +{ + CFDataRef result = NULL; + SecCertificateRef iosCertRef = SecCertificateCreateWithData(allocator, der_certificate); + if (NULL == iosCertRef) + { + return result; + } + + result = SecCertificateCopyPublicKeySHA1Digest(iosCertRef); + CFRelease(iosCertRef); + return result; +} + diff --git a/OSX/libsecurity_keychain/lib/SecCertificateInternalP.h b/OSX/libsecurity_keychain/lib/SecCertificateInternalP.h deleted file mode 100644 index 276de229..00000000 --- a/OSX/libsecurity_keychain/lib/SecCertificateInternalP.h +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2007-2011,2013-2015 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - SecCertificateInternal.h -*/ - -#ifndef _SECURITY_SECCERTIFICATEINTERNAL_H_ -#define _SECURITY_SECCERTIFICATEINTERNAL_H_ - -//#include -#include "SecCertificatePrivP.h" -#include "certextensionsP.h" -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -CFDataRef SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate); - -/* Return an array of CFURLRefs each of which is an crl distribution point for - this certificate. */ -CFArrayRef SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate); - -/* Return an array of CFURLRefs each of which is an ocspResponder for this - certificate. */ -CFArrayRef SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate); - -/* Return an array of CFURLRefs each of which is an caIssuer for this - certificate. */ -CFArrayRef SecCertificateGetCAIssuersP(SecCertificateRefP certificate); - -/* Dump certificate for debugging. */ -void SecCertificateShowP(SecCertificateRefP certificate); - -/* Return the DER encoded issuer sequence for the receiving certificates issuer. */ -CFDataRef SecCertificateCopyIssuerSequenceP(SecCertificateRefP certificate); - -/* Return the DER encoded subject sequence for the receiving certificates subject. */ -CFDataRef SecCertificateCopySubjectSequenceP(SecCertificateRefP certificate); - -/* Return the content of a DER encoded X.501 name (without the tag and length - fields) for the receiving certificates issuer. */ -CFDataRef SecCertificateGetNormalizedIssuerContentP(SecCertificateRefP certificate); - -/* Return the content of a DER encoded X.501 name (without the tag and length - fields) for the receiving certificates subject. */ -CFDataRef SecCertificateGetNormalizedSubjectContentP(SecCertificateRefP certificate); - -CFDataRef SecDERItemCopySequenceP(DERItem *content); - -/* Return true iff the certificate has a subject. */ -bool SecCertificateHasSubjectP(SecCertificateRefP certificate); - -/* Return true iff the certificate has a critical subject alt name. */ -bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate); - -/* Return true if certificate contains one or more critical extensions we - are unable to parse. */ -bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate); - -/* Return true iff certificate is valid as of verifyTime. */ -bool SecCertificateIsValidP(SecCertificateRefP certificate, - CFAbsoluteTime verifyTime); - -/* Return an attribute dictionary used to store this item in a keychain. */ -CFDictionaryRef SecCertificateCopyAttributeDictionaryP( - SecCertificateRefP certificate); - -/* Return a certificate from the attribute dictionary that was used to store - this item in a keychain. */ -SecCertificateRefP SecCertificateCreateFromAttributeDictionaryP( - CFDictionaryRef refAttributes); - -/* Return a SecKeyRef for the public key embedded in the cert. */ -SecKeyRefP SecCertificateCopyPublicKeyP(SecCertificateRefP certificate); - -/* Return the SecCEBasicConstraints extension for this certificate if it - has one. */ -const SecCEBasicConstraints * -SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate); - -/* Return the SecCEPolicyConstraints extension for this certificate if it - has one. */ -const SecCEPolicyConstraints * -SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate); - -/* Return a dictionary from CFDataRef to CFArrayRef of CFDataRef - representing the policyMapping extension of this certificate. */ -CFDictionaryRef -SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate); - -/* Return the SecCECertificatePolicies extension for this certificate if it - has one. */ -const SecCECertificatePolicies * -SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate); - -/* Returns UINT32_MAX if InhibitAnyPolicy extension is not present or invalid, - returns the value of the SkipCerts field of the InhibitAnyPolicy extension - otherwise. */ -uint32_t -SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate); - -/* Return the public key algorithm and parameters for certificate. */ -const DERAlgorithmId *SecCertificateGetPublicKeyAlgorithmP( - SecCertificateRefP certificate); - -/* Return the raw public key data for certificate. */ -const DERItem *SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate); - -#pragma mark - -#pragma mark Certificate Operations - -OSStatus SecCertificateIsSignedByP(SecCertificateRefP certificate, - SecKeyRefP issuerKey); - -#pragma mark - -#pragma mark Certificate Creation - -#ifdef OPTIONAL_METHODS -/* Return a certificate for the PEM representation of this certificate. - Return NULL the passed in der_certificate is not a valid DER encoded X.509 - certificate, and return a CFError by reference. It is the - responsibility of the caller to release the CFError. */ -SecCertificateRefP SecCertificateCreateWithPEMP(CFAllocatorRef allocator, - CFStringRef pem_certificate); - -/* Return a CFStringRef containing the the pem representation of this - certificate. */ -CFStringRef SecCertificateGetPEMP(SecCertificateRefP der_certificate); - -#endif /* OPTIONAL_METHODS */ - -#if 0 -/* Complete the certificate chain of this certificate, setting the parent - certificate for each certificate along they way. Return 0 if the - system is able to find all the certificates to complete the certificate - chain either in the passed in other_certificates array or in the user or - the systems keychain(s). - If the certificate's issuer chain can not be completed, this function - will return an error status code. - NOTE: This function does not verify whether the certificate is trusted it's - main use is just to ensure that anyone using this certificate upstream will - have access to a complete (or as complete as possible in the case of - something going wrong) certificate chain. */ -OSStatus SecCertificateCompleteChainP(SecCertificateRefP certificate, - CFArrayRef other_certificates); -#endif - -#if 0 - -/*! - @function SecCertificateGetVersionNumberP - @abstract Retrieves the version of a given certificate as a CFNumberRef. - @param certificate A reference to the certificate from which to obtain the certificate version. - @result A CFNumberRef representing the certificate version. The following values are currently known to be returned, but more may be added in the future: - 1: X509v1 - 2: X509v2 - 3: X509v3 -*/ -CFNumberRef SecCertificateGetVersionNumberP(SecCertificateRefP certificate); - -/*! - @function SecCertificateGetSerialDERP - @abstract Retrieves the serial number of a given certificate in DER encoding. - @param certificate A reference to the certificate from which to obtain the serial number. - @result A CFDataRef containing the DER encoded serial number of the certificate, minus the tag and length fields. -*/ -CFDataRef SecCertificateGetSerialDERP(SecCertificateRefP certificate); - - -/*! - @function SecCertificateGetSerialStringP - @abstract Retrieves the serial number of a given certificate in human readable form. - @param certificate A reference to the certificate from which to obtain the serial number. - @result A CFStringRef containing the human readable serial number of the certificate in decimal form. -*/ -CFStringRef SecCertificateGetSerialStringP(SecCertificateRefP certificate); - - - -CFDataRef SecCertificateGetPublicKeyDERP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetPublicKeySHA1FingerPrintP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetPublicKeyMD5FingerPrintP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetSignatureAlgorithmDERP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetSignatureAlgorithmNameP(SecCertificateRefP certificate); -CFStringRef SecCertificateGetSignatureAlgorithmOIDP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetSignatureDERP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetSignatureAlgorithmParametersDERP(SecCertificateRefP certificate); - -/* plist top level array is orderd list of key/value pairs */ -CFArrayRef SecCertificateGetSignatureAlgorithmParametersArrayP(SecCertificateRefP certificate); - -#if 0 -/* This cert is signed by its parent? */ -bool SecCertificateIsSignatureValidP(SecCertificateRefP certificate); - -/* This cert is signed by its parent and so on until no parent certificate can be found? */ -bool SecCertificateIsIssuerChainValidP(SecCertificateRefP certificate, CFArrayRef additionalCertificatesToSearch); - -/* This cert is signed by its parent and so on until no parent certificate can be found? */ -bool SecCertificateIsSignatureChainValidP(SecCertificateRefP certificate); - -/* This cert is signed by its parent and so on until a certiicate in anchors can be found. */ -bool SecCertificateIssuerChainHasAnchorInP(SecCertificateRefP certificate, CFArrayRef anchors); - -/* This cert is signed by its parent and so on until a certiicate in anchors can be found. */ -bool SecCertificateSignatureChainHasAnchorInP(SecCertificateRefP certificate, CFArrayRef anchors); - -bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate); -#endif - - -/* The entire certificate in DER encoding including the outer tag and length fields. */ -CFDataRef SecCertificateGetDERP(SecCertificateRefP certificate); - -/* Returns the status code of the last failed call for this certificate on this thread. */ -OSStatus SecCertificateGetStatusP(SecCertificateRefP certificate); - -CFDataRef SecCertificateGetIssuerDERP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetNormalizedIssuerDERP(SecCertificateRefP certificate); - -/* Return the issuer as an X509 name encoded in an array. Each element in this array is an array. Each inner array has en even number of elements. Each pair of elements in the inner array represents a key and a value. The key is a string and the value is also a string. Elements in the outer array should be considered ordered while pairs in the inner array should not. */ -CFArrayRef SecCertificateGetIssuerArrayP(SecCertificateRefP certificate); - - -CFDataRef SecCertificateGetSubjectDERP(SecCertificateRefP certificate); -CFDataRef SecCertificateGetNormalizedSubjectDERP(SecCertificateRefP certificate); -/* See SecCertificateGetIssuerArray for a description of the returned array. */ -CFArrayRef SecCertificateGetSubjectArrayP(SecCertificateRefP certificate); - -CFDateRef SecCertificateGetNotValidBeforeDateP(SecCertificateRefP certificate); -CFDateRef SecCertificateGetNotValidDateP(SecCertificateRefP certificate); - - -#if 0 - -CFIndex SecCertificateGetExtensionCountP(SecCertificateRefP certificate, index); -CFDataRef SecCertificateGetExtensionAtIndexDERP(SecCertificateRefP certificate, CFIndex index); -bool SecCertificateIsExtensionAtIndexCriticalP(SecCertificateRefP certificate, CFIndex index); - -/* array see email example. */ -CFArrayRef SecCertificateGetExtensionAtIndexParamsArrayP(SecCertificateRefP certificate, CFIndex index); - -CFStringRef SecCertificateGetExtensionAtIndexNameP(SecCertificateRefP certificate, CFIndex index); -CFStringRef SecCertificateGetExtensionAtIndexOIDP(SecCertificateRefP certificate, CFIndex index); - -#else - -/* Return an array with all of this certificates SecCertificateExtensionRefs. */ -CFArrayRef SecCertificateGetExtensionsP(SecCertificateRefP certificate); - -/* Return the SecCertificateExtensionRef for the extension with the given oid. Return NULL if it does not exist or if an error occours call SecCertificateGetStatus() to see if an error occured or not. */ -SecCertificateExtensionRef SecCertificateGetExtensionWithOIDP(SecCertificateRefP certificate, CFDataRef oid); - -CFDataRef SecCertificateExtensionGetDERP(SecCertificateExtensionRef extension, CFDataRef oid); -CFStringRef SecCertificateExtensionNameP(SecCertificateExtensionRef extension); -CFDataRef SecCertificateExtensionGetOIDDERP(SecCertificateExtensionRef extension, CFDataRef oid); -CFStringRef SecCertificateExtensionGetOIDStringP(SecCertificateExtensionRef extension, CFDataRef oid); -bool SecCertificateExtensionIsCriticalP(SecCertificateExtensionRef extension); -CFArrayRef SecCertificateExtensionGetContentDERP(SecCertificateExtensionRef extension); - -/* Return the content of extension as an array. The array has en even number of elements. Each pair of elements in the array represents a key and a value. The key is a string and the value is either a string, or dictionary or an array of key value pairs like the outer array. */ -CFArrayRef SecCertificateExtensionGetContentArrayP(SecCertificateExtensionRef extension); - -#endif /* 0 */ - -#endif /* 0 */ - - -void appendPropertyP(CFMutableArrayRef properties, - CFStringRef propertyType, CFStringRef label, CFTypeRef value); - -#if 0 -/* Utility functions. */ -CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator, - const DERItem *oid); -CFDataRef createNormalizedX501Name(CFAllocatorRef allocator, - const DERItem *x501name); - -/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return - an absoluteTime if the date was valid and properly decoded. Return - NULL_TIME otherwise. */ -CFAbsoluteTime SecAbsoluteTimeFromDateContent(DERTag tag, const uint8_t *bytes, - size_t length); -#endif - -#if defined(__cplusplus) -} -#endif - -#endif /* !_SECURITY_SECCERTIFICATEINTERNAL_H_ */ diff --git a/OSX/libsecurity_keychain/lib/SecCertificateP.c b/OSX/libsecurity_keychain/lib/SecCertificateP.c deleted file mode 100644 index 77210b57..00000000 --- a/OSX/libsecurity_keychain/lib/SecCertificateP.c +++ /dev/null @@ -1,4762 +0,0 @@ -/* - * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - * SecCertificate.c - CoreFoundation based certificate object - */ - - -#include "SecCertificateInternalP.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "SecBasePriv.h" - -#include "SecRSAKeyP.h" -#include "SecFrameworkP.h" -#include "SecItem.h" -#include "SecItemPriv.h" -#include -#include -#include -#include -#include "SecInternal.h" -#include "SecBase64P.h" - -#include - -typedef struct SecCertificateExtension { - DERItem extnID; - bool critical; - DERItem extnValue; -} SecCertificateExtension; - -#if 0 -typedef struct KnownExtension { - bool critical; - DERItem extnValue; -} KnownExtension; - -enum { - kSecSelfSignedUnknown = 0, - kSecSelfSignedFalse, - kSecSelfSignedTrue, -}; -#endif - -struct __SecCertificate { - CFRuntimeBase _base; - - DERItem _der; /* Entire certificate in DER form. */ - DERItem _tbs; /* To Be Signed cert DER bytes. */ - DERAlgorithmId _sigAlg; /* Top level signature algorithm. */ - DERItem _signature; /* The content of the sig bit string. */ - - UInt8 _version; - DERItem _serialNum; /* Integer. */ - DERAlgorithmId _tbsSigAlg; /* sig alg MUST be same as _sigAlg. */ - DERItem _issuer; /* Sequence of RDN. */ - CFAbsoluteTime _notBefore; - CFAbsoluteTime _notAfter; - DERItem _subject; /* Sequence of RDN. */ - DERAlgorithmId _algId; /* oid and params of _pubKeyDER. */ - DERItem _pubKeyDER; /* contents of bit string */ - DERItem _issuerUniqueID; /* bit string, optional */ - DERItem _subjectUniqueID; /* bit string, optional */ - -#if 0 - /* Known extensions if the certificate contains them, - extnValue.length will be > 0. */ - KnownExtension _authorityKeyID; - - /* This extension is used to uniquely identify a certificate from among - several that have the same subject name. If the extension is not - present, its value is calculated by performing a SHA-1 hash of the - certificate's DER encoded subjectPublicKeyInfo, as recommended by - PKIX. */ - KnownExtension _subjectKeyID; - KnownExtension _keyUsage; - KnownExtension _extendedKeyUsage; - KnownExtension _basicConstraints; - KnownExtension _netscapeCertType; - KnownExtension _subjectAltName; - KnownExtension _qualCertStatements; - -#endif - bool _foundUnknownCriticalExtension; - - /* Well known certificate extensions. */ - SecCEBasicConstraints _basicConstraints; - SecCEPolicyConstraints _policyConstraints; - CFDictionaryRef _policyMappings; - SecCECertificatePolicies _certificatePolicies; - - /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX, - value of the SkipCerts field of the InhibitAnyPolicy extension - otherwise. */ - uint32_t _inhibitAnyPolicySkipCerts; - - /* If KeyUsage extension is not present this is 0, otherwise it's - the value of the extension. */ - SecKeyUsage _keyUsage; - - /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier. - Length = 0 if not present. */ - DERItem _subjectKeyIdentifier; - - /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier. - Length = 0 if not present. */ - DERItem _authorityKeyIdentifier; - /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and - _authorityKeyIdentifierSerialNumber have non zero length if present. - Both are either present or absent together. */ - DERItem _authorityKeyIdentifierIssuer; - DERItem _authorityKeyIdentifierSerialNumber; - - /* Subject alt name extension, if present. Not malloced, it's just a - pointer to an element in the _extensions array. */ - const SecCertificateExtension *_subjectAltName; - - /* Parsed extension values. */ - - /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */ - CFMutableArrayRef _crlDistributionPoints; - - /* Array of CFURLRefs containing the URI values of accessLocations of each - id-ad-ocsp AccessDescription in the Authority Information Access - extension. */ - CFMutableArrayRef _ocspResponders; - - /* Array of CFURLRefs containing the URI values of accessLocations of each - id-ad-caIssuers AccessDescription in the Authority Information Access - extension. */ - CFMutableArrayRef _caIssuers; - - /* All other (non known) extensions. The _extensions array is malloced. */ - CFIndex _extensionCount; - SecCertificateExtension *_extensions; - - /* Optional cached fields. */ - SecKeyRef _pubKey; - CFDataRef _der_data; - CFArrayRef _properties; - CFDataRef _serialNumber; - CFDataRef _normalizedIssuer; - CFDataRef _normalizedSubject; - CFDataRef _authorityKeyID; - CFDataRef _subjectKeyID; - - CFDataRef _sha1Digest; - uint8_t _isSelfSigned; - -}; - -/* Public Constants for property list keys. */ -const CFStringRef kSecPropertyKeyType = CFSTR("type"); -const CFStringRef kSecPropertyKeyLabel = CFSTR("label"); -const CFStringRef kSecPropertyKeyLocalizedLabel = CFSTR("localized label"); -const CFStringRef kSecPropertyKeyValue = CFSTR("value"); - -/* Public Constants for property list values. */ -const CFStringRef kSecPropertyTypeWarning = CFSTR("warning"); -const CFStringRef kSecPropertyTypeError = CFSTR("error"); -const CFStringRef kSecPropertyTypeSuccess = CFSTR("success"); -const CFStringRef kSecPropertyTypeTitle = CFSTR("title"); -const CFStringRef kSecPropertyTypeSection = CFSTR("section"); -const CFStringRef kSecPropertyTypeData = CFSTR("data"); -const CFStringRef kSecPropertyTypeString = CFSTR("string"); -const CFStringRef kSecPropertyTypeURL = CFSTR("url"); -const CFStringRef kSecPropertyTypeDate = CFSTR("date"); - -/* Extension parsing routine. */ -typedef void (*SecCertificateExtensionParser)(SecCertificateRefP certificate, - const SecCertificateExtension *extn); - -/* CFRuntime regsitration data. */ -static pthread_once_t kSecCertificateRegisterClass = PTHREAD_ONCE_INIT; -static CFTypeID kSecCertificateTypeID = _kCFRuntimeNotATypeID; - -/* Mapping from extension OIDs (as a DERItem *) to - SecCertificateExtensionParser extension parsing routines. */ -static CFDictionaryRef gExtensionParsers; - -/* Forward declartions of static functions. */ -static CFStringRef SecCertificateCopyDescription(CFTypeRef cf); -static void SecCertificateDestroy(CFTypeRef cf); -static bool derDateGetAbsoluteTime(const DERItem *dateChoice, - CFAbsoluteTime *absTime); - -/* Static functions. */ -static CFStringRef SecCertificateCopyDescription(CFTypeRef cf) { - SecCertificateRefP certificate = (SecCertificateRefP)cf; - CFStringRef ret = NULL; - CFStringRef subjectSummary = SecCertificateCopySubjectSummaryP(certificate); - CFStringRef issuerSummary = SecCertificateCopyIssuerSummaryP(certificate); - - ret = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR(""), certificate, - subjectSummary, - issuerSummary); - - CFReleaseNull(subjectSummary); - CFReleaseNull(issuerSummary); - return ret; -} - -static void SecCertificateDestroy(CFTypeRef cf) { - SecCertificateRefP certificate = (SecCertificateRefP)cf; - if (certificate->_certificatePolicies.policies) { - free(certificate->_certificatePolicies.policies); - certificate->_certificatePolicies.policies = NULL; - } - CFReleaseNull(certificate->_policyMappings); - CFReleaseNull(certificate->_crlDistributionPoints); - CFReleaseNull(certificate->_ocspResponders); - CFReleaseNull(certificate->_caIssuers); - if (certificate->_extensions) { - free(certificate->_extensions); - certificate->_extensions = NULL; - } - CFReleaseNull(certificate->_pubKey); - CFReleaseNull(certificate->_der_data); - CFReleaseNull(certificate->_properties); - CFReleaseNull(certificate->_serialNumber); - CFReleaseNull(certificate->_normalizedIssuer); - CFReleaseNull(certificate->_normalizedSubject); - CFReleaseNull(certificate->_authorityKeyID); - CFReleaseNull(certificate->_subjectKeyID); - CFReleaseNull(certificate->_sha1Digest); -} - -static Boolean SecCertificateEqual(CFTypeRef cf1, CFTypeRef cf2) { - SecCertificateRefP cert1 = (SecCertificateRefP)cf1; - SecCertificateRefP cert2 = (SecCertificateRefP)cf2; - if (cert1 == cert2) - return true; - if (!cert2 || cert1->_der.length != cert2->_der.length) - return false; - return !memcmp(cert1->_der.data, cert2->_der.data, cert1->_der.length); -} - -/* Hash of the certificate is der length + signature length + last 4 bytes - of signature. */ -static CFHashCode SecCertificateHash(CFTypeRef cf) { - SecCertificateRefP certificate = (SecCertificateRefP)cf; - DERSize der_length = certificate->_der.length; - DERSize sig_length = certificate->_signature.length; - DERSize ix = (sig_length > 4) ? sig_length - 4 : 0; - CFHashCode hashCode = 0; - for (; ix < sig_length; ++ix) - hashCode = (hashCode << 8) + certificate->_signature.data[ix]; - - return (hashCode + der_length + sig_length); -} - -#if 1 - -/************************************************************************/ -/************************* General Name Parsing *************************/ -/************************************************************************/ - -typedef OSStatus (*parseGeneralNameCallback)(void *context, - SecCEGeneralNameType type, const DERItem *value); - - -/* - GeneralName ::= CHOICE { - otherName [0] OtherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER} - - OtherName ::= SEQUENCE { - type-id OBJECT IDENTIFIER, - value [0] EXPLICIT ANY DEFINED BY type-id } - - EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - */ -static OSStatus parseGeneralNameContentProperty(DERTag tag, - const DERItem *generalNameContent, - void *context, parseGeneralNameCallback callback) { - switch (tag) { - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0: - return callback(context, GNT_OtherName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 1: - return callback(context, GNT_RFC822Name, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 2: - return callback(context, GNT_DNSName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3: - return callback(context, GNT_X400Address, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4: - return callback(context, GNT_DirectoryName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5: - return callback(context, GNT_EdiPartyName, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - { - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ - DERDecodedInfo uriContent; - require_noerr(DERDecodeItem(generalNameContent, &uriContent), badDER); - require(uriContent.tag == ASN1_IA5_STRING, badDER); - return callback(context, GNT_URI, &uriContent.content); - } - case ASN1_CONTEXT_SPECIFIC | 6: - return callback(context, GNT_URI, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 7: - return callback(context, GNT_IPAddress, generalNameContent); - case ASN1_CONTEXT_SPECIFIC | 8: - return callback(context, GNT_RegisteredID, generalNameContent); - default: - goto badDER; - } -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseGeneralNamesContent(const DERItem *generalNamesContent, - void *context, parseGeneralNameCallback callback) { - DERSequence gnSeq; - DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo generalNameContent; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - OSStatus status = parseGeneralNameContentProperty( - generalNameContent.tag, &generalNameContent.content, context, - callback); - if (status) - return status; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return errSecSuccess; - -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseGeneralNames(const DERItem *generalNames, void *context, - parseGeneralNameCallback callback) { - DERDecodedInfo generalNamesContent; - DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent); - require_noerr_quiet(drtn, badDER); - require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - return parseGeneralNamesContent(&generalNamesContent.content, context, - callback); -badDER: - return errSecInvalidCertificate; -} - -#else - -/* - GeneralName ::= CHOICE { - otherName [0] OtherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER} - - EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - */ -static OSStatus parseGeneralNameContentProperty(DERTag tag, - const DERItem *generalNameContent, SecCEGeneralName *generalName) { - switch (tag) { - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0: - generalName->nameType = GNT_OtherName; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 1: - /* IA5String. */ - generalName->nameType = GNT_RFC822Name; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 2: - /* IA5String. */ - generalName->nameType = GNT_DNSName; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3: - generalName->nameType = GNT_X400Address; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4: - generalName->nameType = GNT_DirectoryName; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5: - generalName->nameType = GNT_EdiPartyName; - generalName->berEncoded = true; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - { - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ - DERDecodedInfo decoded; - require_noerr(DERDecodeItem(generalNameContent, &decoded), badDER); - require(decoded.tag == ASN1_IA5_STRING, badDER); - generalName->nameType = GNT_URI; - generalName->berEncoded = false; - generalName->name = decoded.content; - break; - } - case ASN1_CONTEXT_SPECIFIC | 6: - generalName->nameType = GNT_URI; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 7: - /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's - 8 octects, addr/mask for ipv6 it's 32. */ - generalName->nameType = GNT_IPAddress; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - case ASN1_CONTEXT_SPECIFIC | 8: - /* name is the content of an OID. */ - generalName->nameType = GNT_RegisteredID; - generalName->berEncoded = false; - generalName->name = *generalNameContent; - break; - default: - goto badDER; - break; - } - return errSecSuccess; -badDER: - return errSecInvalidCertificate; -} - -/* - GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - */ -static OSStatus parseGeneralNamesContent(const DERItem *generalNamesContent, - CFIndex *count, SecCEGeneralName **name) { - SecCEGeneralName *generalNames = NULL; - DERSequence gnSeq; - DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo generalNameContent; - CFIndex generalNamesCount = 0; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - ++generalNamesCount; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - - require(generalNames = calloc(generalNamesCount, sizeof(SecCEGeneralName)), - badDER); - DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - CFIndex ix = 0; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - if (!parseGeneralNameContentProperty(generalNameContent.tag, - &generalNameContent.content, &generalNames[ix])) { - goto badDER; - } - ++ix; - } - *count = generalNamesCount; - *name = generalNames; - return errSecSuccess; - -badDER: - if (generalNames) - free(generalNames); - return errSecInvalidCertificate; -} - -static OSStatus parseGeneralNames(const DERItem *generalNames, - CFIndex *count, SecCEGeneralName **name) { - DERDecodedInfo generalNamesContent; - DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent); - require_noerr_quiet(drtn, badDER); - require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, - badDER); - parseGeneralNamesContent(&generalNamesContent.content, count, name); - return errSecSuccess; -badDER: - return errSecInvalidCertificate; -} -#endif - -/************************************************************************/ -/************************** X.509 Name Parsing **************************/ -/************************************************************************/ - -typedef OSStatus (*parseX501NameCallback)(void *context, const DERItem *type, - const DERItem *value, CFIndex rdnIX); - -static OSStatus parseRDNContent(const DERItem *rdnSetContent, void *context, - parseX501NameCallback callback) { - DERSequence rdn; - DERReturn drtn = DERDecodeSeqContentInit(rdnSetContent, &rdn); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo atvContent; - CFIndex rdnIX = 0; - while ((drtn = DERDecodeSeqNext(&rdn, &atvContent)) == DR_Success) { - require_quiet(atvContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERAttributeTypeAndValue atv; - drtn = DERParseSequenceContent(&atvContent.content, - DERNumAttributeTypeAndValueItemSpecs, - DERAttributeTypeAndValueItemSpecs, - &atv, sizeof(atv)); - require_noerr_quiet(drtn, badDER); - require_quiet(atv.type.length != 0, badDER); - OSStatus status = callback(context, &atv.type, &atv.value, rdnIX++); - if (status) - return status; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - - return errSecSuccess; -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseX501NameContent(const DERItem *x501NameContent, void *context, - parseX501NameCallback callback) { - DERSequence derSeq; - DERReturn drtn = DERDecodeSeqContentInit(x501NameContent, &derSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo currDecoded; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { - require_quiet(currDecoded.tag == ASN1_CONSTR_SET, badDER); - OSStatus status = parseRDNContent(&currDecoded.content, context, - callback); - if (status) - return status; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - - return errSecSuccess; - -badDER: - return errSecInvalidCertificate; -} - -static OSStatus parseX501Name(const DERItem *x501Name, void *context, - parseX501NameCallback callback) { - DERDecodedInfo x501NameContent; - if (DERDecodeItem(x501Name, &x501NameContent) || - x501NameContent.tag != ASN1_CONSTR_SEQUENCE) { - return errSecInvalidCertificate; - } else { - return parseX501NameContent(&x501NameContent.content, context, - callback); - } -} - -/************************************************************************/ -/********************** Extension Parsing Routines **********************/ -/************************************************************************/ - -static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERDecodedInfo keyIdentifier; - DERReturn drtn = DERDecodeItem(&extn->extnValue, &keyIdentifier); - require_noerr_quiet(drtn, badDER); - require_quiet(keyIdentifier.tag == ASN1_OCTET_STRING, badDER); - certificate->_subjectKeyIdentifier = keyIdentifier.content; - - return; -badDER: - secinfo("cert", "Invalid SubjectKeyIdentifier Extension"); -} - -static void SecCEPKeyUsage(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - SecKeyUsage keyUsage = extn->critical ? kSecKeyUsageCritical : 0; - DERDecodedInfo bitStringContent; - DERReturn drtn = DERDecodeItem(&extn->extnValue, &bitStringContent); - require_noerr_quiet(drtn, badDER); - require_quiet(bitStringContent.tag == ASN1_BIT_STRING, badDER); - DERSize len = bitStringContent.content.length - 1; - require_quiet(len == 1 || len == 2, badDER); - DERByte numUnusedBits = bitStringContent.content.data[0]; - require_quiet(numUnusedBits < 8, badDER); - /* Flip the bits in the bit string so the first bit in the lsb. */ - uint_fast16_t bits = 8 * len - numUnusedBits; - uint_fast16_t value = bitStringContent.content.data[1]; - uint_fast16_t mask; - if (len > 1) { - value = (value << 8) + bitStringContent.content.data[2]; - mask = 0x8000; - } else { - mask = 0x80; - } - uint_fast16_t ix; - for (ix = 0; ix < bits; ++ix) { - if (value & mask) { - keyUsage |= 1 << ix; - } - mask >>= 1; - } - certificate->_keyUsage = keyUsage; - return; -badDER: - certificate->_keyUsage = kSecKeyUsageUnspecified; -} - -static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPSubjectAltName(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - certificate->_subjectAltName = extn; -} - -static void SecCEPIssuerAltName(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPBasicConstraints(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERBasicConstraints basicConstraints; - require_noerr_quiet(DERParseSequence(&extn->extnValue, - DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs, - &basicConstraints, sizeof(basicConstraints)), badDER); - require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints.cA, false, - &certificate->_basicConstraints.isCA), badDER); - if (basicConstraints.pathLenConstraint.length != 0) { - require_noerr_quiet(DERParseInteger( - &basicConstraints.pathLenConstraint, - &certificate->_basicConstraints.pathLenConstraint), badDER); - certificate->_basicConstraints.pathLenConstraintPresent = true; - } - certificate->_basicConstraints.present = true; - certificate->_basicConstraints.critical = extn->critical; - return; -badDER: - certificate->_basicConstraints.present = false; - secinfo("cert", "Invalid BasicConstraints Extension"); -} - -static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -/* - certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation - - PolicyInformation ::= SEQUENCE { - policyIdentifier CertPolicyId, - policyQualifiers SEQUENCE SIZE (1..MAX) OF - PolicyQualifierInfo OPTIONAL } - - CertPolicyId ::= OBJECT IDENTIFIER - - PolicyQualifierInfo ::= SEQUENCE { - policyQualifierId PolicyQualifierId, - qualifier ANY DEFINED BY policyQualifierId } -*/ -/* maximum number of policies of 8192 seems more than adequate */ -#define MAX_CERTIFICATE_POLICIES 8192 -static void SecCEPCertificatePolicies(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence piSeq; - SecCEPolicyInformation *policies = NULL; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo piContent; - DERSize policy_count = 0; - while ((policy_count < MAX_CERTIFICATE_POLICIES) && - (drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) { - require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - policy_count++; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - DERSize malloc_policies = policy_count > 0 ? policy_count : 1; - require_quiet(policies = (SecCEPolicyInformation *)malloc(sizeof(SecCEPolicyInformation) - * malloc_policies), badDER); - drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &piSeq); - require_noerr_quiet(drtn, badDER); - DERSize policy_ix = 0; - while ((policy_ix < (policy_count > 0 ? policy_count : 1)) && - (drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) { - DERPolicyInformation pi; - drtn = DERParseSequenceContent(&piContent.content, - DERNumPolicyInformationItemSpecs, - DERPolicyInformationItemSpecs, - &pi, sizeof(pi)); - require_noerr_quiet(drtn, badDER); - policies[policy_ix].policyIdentifier = pi.policyIdentifier; - policies[policy_ix++].policyQualifiers = pi.policyQualifiers; - } - certificate->_certificatePolicies.present = true; - certificate->_certificatePolicies.critical = extn->critical; - certificate->_certificatePolicies.numPolicies = (uint32_t)policy_count; - certificate->_certificatePolicies.policies = policies; - return; -badDER: - if (policies) - free(policies); - certificate->_certificatePolicies.present = false; - secinfo("cert", "Invalid CertificatePolicies Extension"); -} - -/* - id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } - - PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { - issuerDomainPolicy CertPolicyId, - subjectDomainPolicy CertPolicyId } -*/ -#if 0 -static void SecCEPPolicyMappings(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence pmSeq; - SecCEPolicyMapping *mappings = NULL; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo pmContent; - DERSize mapping_count = 0; - while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) { - require_quiet(pmContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - mapping_count++; - } - mappings = (SecCEPolicyMapping *)malloc(sizeof(SecCEPolicyMapping) - * mapping_count); - drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq); - require_noerr_quiet(drtn, badDER); - DERSize mapping_ix = 0; - while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) { - DERPolicyMapping pm; - drtn = DERParseSequenceContent(&pmContent.content, - DERNumPolicyMappingItemSpecs, - DERPolicyMappingItemSpecs, - &pm, sizeof(pm)); - require_noerr_quiet(drtn, badDER); - mappings[mapping_ix].issuerDomainPolicy = pm.issuerDomainPolicy; - mappings[mapping_ix++].subjectDomainPolicy = pm.subjectDomainPolicy; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - certificate->_policyMappings.present = true; - certificate->_policyMappings.critical = extn->critical; - certificate->_policyMappings.numMappings = mapping_count; - certificate->_policyMappings.mappings = mappings; - return; -badDER: - if (mappings) - free(mappings); - CFReleaseSafe(mappings); - certificate->_policyMappings.present = false; - secinfo("cert", "Invalid CertificatePolicies Extension"); -} -#else -static void SecCEPPolicyMappings(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence pmSeq; - CFMutableDictionaryRef mappings = NULL; - CFDataRef idp = NULL, sdp = NULL; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &pmSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo pmContent; - require_quiet(mappings = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), - badDER);; - while ((drtn = DERDecodeSeqNext(&pmSeq, &pmContent)) == DR_Success) { - require_quiet(pmContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERPolicyMapping pm; - drtn = DERParseSequenceContent(&pmContent.content, - DERNumPolicyMappingItemSpecs, - DERPolicyMappingItemSpecs, - &pm, sizeof(pm)); - require_noerr_quiet(drtn, badDER); - - require_quiet(idp = CFDataCreate(kCFAllocatorDefault, - pm.issuerDomainPolicy.data, pm.issuerDomainPolicy.length), badDER); - require_quiet(sdp = CFDataCreate(kCFAllocatorDefault, - pm.subjectDomainPolicy.data, pm.subjectDomainPolicy.length), badDER); - CFMutableArrayRef sdps = - (CFMutableArrayRef)CFDictionaryGetValue(mappings, idp); - if (sdps) { - CFArrayAppendValue(sdps, sdp); - } else { - require_quiet(sdps = CFArrayCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeArrayCallBacks), badDER); - CFDictionarySetValue(mappings, idp, sdps); - CFRelease(sdps); - } - CFReleaseNull(idp); - CFReleaseNull(sdp); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - certificate->_policyMappings = mappings; - return; -badDER: - CFReleaseNull(idp); - CFReleaseNull(sdp); - CFReleaseSafe(mappings); - certificate->_policyMappings = NULL; - secinfo("cert", "Invalid CertificatePolicies Extension"); -} -#endif - -/* -AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - -- authorityCertIssuer and authorityCertSerialNumber MUST both - -- be present or both be absent - -KeyIdentifier ::= OCTET STRING -*/ -static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERAuthorityKeyIdentifier akid; - DERReturn drtn; - drtn = DERParseSequence(&extn->extnValue, - DERNumAuthorityKeyIdentifierItemSpecs, - DERAuthorityKeyIdentifierItemSpecs, - &akid, sizeof(akid)); - require_noerr_quiet(drtn, badDER); - if (akid.keyIdentifier.length) { - certificate->_authorityKeyIdentifier = akid.keyIdentifier; - } - if (akid.authorityCertIssuer.length || - akid.authorityCertSerialNumber.length) { - require_quiet(akid.authorityCertIssuer.length && - akid.authorityCertSerialNumber.length, badDER); - /* Perhaps put in a subsection called Authority Certificate Issuer. */ - certificate->_authorityKeyIdentifierIssuer = akid.authorityCertIssuer; - certificate->_authorityKeyIdentifierSerialNumber = akid.authorityCertSerialNumber; - } - - return; -badDER: - secinfo("cert", "Invalid AuthorityKeyIdentifier Extension"); -} - -static void SecCEPPolicyConstraints(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERPolicyConstraints pc; - DERReturn drtn; - drtn = DERParseSequence(&extn->extnValue, - DERNumPolicyConstraintsItemSpecs, - DERPolicyConstraintsItemSpecs, - &pc, sizeof(pc)); - require_noerr_quiet(drtn, badDER); - if (pc.requireExplicitPolicy.length) { - require_noerr_quiet(DERParseInteger( - &pc.requireExplicitPolicy, - &certificate->_policyConstraints.requireExplicitPolicy), badDER); - certificate->_policyConstraints.requireExplicitPolicyPresent = true; - } - if (pc.inhibitPolicyMapping.length) { - require_noerr_quiet(DERParseInteger( - &pc.inhibitPolicyMapping, - &certificate->_policyConstraints.inhibitPolicyMapping), badDER); - certificate->_policyConstraints.inhibitPolicyMappingPresent = true; - } - - certificate->_policyConstraints.present = true; - certificate->_policyConstraints.critical = extn->critical; - - return; -badDER: - certificate->_policyConstraints.present = false; - secinfo("cert", "Invalid PolicyConstraints Extension"); -} - -static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -/* - InhibitAnyPolicy ::= SkipCerts - - SkipCerts ::= INTEGER (0..MAX) -*/ -static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - require_noerr_quiet(DERParseInteger( - &extn->extnValue, - &certificate->_inhibitAnyPolicySkipCerts), badDER); - return; -badDER: - certificate->_inhibitAnyPolicySkipCerts = UINT32_MAX; - secinfo("cert", "Invalid InhibitAnyPolicy Extension"); -} - -/* - id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } - - AuthorityInfoAccessSyntax ::= - SEQUENCE SIZE (1..MAX) OF AccessDescription - - AccessDescription ::= SEQUENCE { - accessMethod OBJECT IDENTIFIER, - accessLocation GeneralName } - - id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } - - id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } - - id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } - */ -static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); - DERTag tag; - DERSequence adSeq; - DERReturn drtn = DERDecodeSeqInit(&extn->extnValue, &tag, &adSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo adContent; - while ((drtn = DERDecodeSeqNext(&adSeq, &adContent)) == DR_Success) { - require_quiet(adContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERAccessDescription ad; - drtn = DERParseSequenceContent(&adContent.content, - DERNumAccessDescriptionItemSpecs, - DERAccessDescriptionItemSpecs, - &ad, sizeof(ad)); - require_noerr_quiet(drtn, badDER); - CFMutableArrayRef *urls; - if (DEROidCompare(&ad.accessMethod, &oidAdOCSP)) - urls = &certificate->_ocspResponders; - else if (DEROidCompare(&ad.accessMethod, &oidAdCAIssuer)) - urls = &certificate->_caIssuers; - else - continue; - - DERDecodedInfo generalNameContent; - drtn = DERDecodeItem(&ad.accessLocation, &generalNameContent); - require_noerr_quiet(drtn, badDER); - switch (generalNameContent.tag) { -#if 0 - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ -#endif - case ASN1_CONTEXT_SPECIFIC | 6: - { - CFURLRef url = CFURLCreateWithBytes(kCFAllocatorDefault, - generalNameContent.content.data, generalNameContent.content.length, - kCFStringEncodingASCII, NULL); - if (url) { - if (!*urls) - *urls = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - CFArrayAppendValue(*urls, url); - CFRelease(url); - } - break; - } - default: - secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s", - generalNameContent.tag, (int)generalNameContent.content.length, generalNameContent.content.data); - goto badDER; - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - secinfo("cert", "failed to parse Authority Information Access extension"); -} - -static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPNetscapeCertType(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -static void SecCEPEntrustVersInfo(SecCertificateRefP certificate, - const SecCertificateExtension *extn) { - secinfo("cert", "critical: %s", extn->critical ? "yes" : "no"); -} - -/* Dictionary key callback for comparing to DERItems. */ -static Boolean SecDERItemEqual(const void *value1, const void *value2) { - return DEROidCompare((const DERItem *)value1, (const DERItem *)value2); -} - -/* Dictionary key callback calculating the hash of a DERItem. */ -static CFHashCode SecDERItemHash(const void *value) { - const DERItem *derItem = (const DERItem *)value; - CFHashCode hash = derItem->length; - DERSize ix = derItem->length > 8 ? derItem->length - 8 : 0; - for (; ix < derItem->length; ++ix) { - hash = (hash << 9) + (hash >> 23) + derItem->data[ix]; - } - - return hash; -} - -/* Dictionary key callbacks using the above 2 functions. */ -static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks = { - 0, /* version */ - NULL, /* retain */ - NULL, /* release */ - NULL, /* copyDescription */ - SecDERItemEqual, /* equal */ - SecDERItemHash /* hash */ -}; - -static void SecCertificateRegisterClass(void) { - static const CFRuntimeClass kSecCertificateClass = { - 0, /* version */ - "SecCertificate", /* class name */ - NULL, /* init */ - NULL, /* copy */ - SecCertificateDestroy, /* dealloc */ - SecCertificateEqual, /* equal */ - SecCertificateHash, /* hash */ - NULL, /* copyFormattingDesc */ - SecCertificateCopyDescription /* copyDebugDesc */ - }; - - kSecCertificateTypeID = _CFRuntimeRegisterClass(&kSecCertificateClass); - - /* Build a dictionary that maps from extension OIDs to callback functions - which can parse the extension of the type given. */ - static const void *extnOIDs[] = { - &oidSubjectKeyIdentifier, - &oidKeyUsage, - &oidPrivateKeyUsagePeriod, - &oidSubjectAltName, - &oidIssuerAltName, - &oidBasicConstraints, - &oidCrlDistributionPoints, - &oidCertificatePolicies, - &oidPolicyMappings, - &oidAuthorityKeyIdentifier, - &oidPolicyConstraints, - &oidExtendedKeyUsage, - &oidInhibitAnyPolicy, - &oidAuthorityInfoAccess, - &oidSubjectInfoAccess, - &oidNetscapeCertType, - &oidEntrustVersInfo - }; - static const void *extnParsers[] = { - SecCEPSubjectKeyIdentifier, - SecCEPKeyUsage, - SecCEPPrivateKeyUsagePeriod, - SecCEPSubjectAltName, - SecCEPIssuerAltName, - SecCEPBasicConstraints, - SecCEPCrlDistributionPoints, - SecCEPCertificatePolicies, - SecCEPPolicyMappings, - SecCEPAuthorityKeyIdentifier, - SecCEPPolicyConstraints, - SecCEPExtendedKeyUsage, - SecCEPInhibitAnyPolicy, - SecCEPAuthorityInfoAccess, - SecCEPSubjectInfoAccess, - SecCEPNetscapeCertType, - SecCEPEntrustVersInfo - }; - gExtensionParsers = CFDictionaryCreate(kCFAllocatorDefault, extnOIDs, - extnParsers, sizeof(extnOIDs) / sizeof(*extnOIDs), - &SecDERItemKeyCallBacks, NULL); -} - -/* Given the contents of an X.501 Name return the contents of a normalized - X.501 name. */ -static CFDataRef createNormalizedX501Name(CFAllocatorRef allocator, - const DERItem *x501name) { - CFMutableDataRef result = CFDataCreateMutable(allocator, x501name->length); - CFIndex length = x501name->length; - CFDataSetLength(result, length); - UInt8 *base = CFDataGetMutableBytePtr(result); - - DERSequence rdnSeq; - DERReturn drtn = DERDecodeSeqContentInit(x501name, &rdnSeq); - - require_noerr_quiet(drtn, badDER); - DERDecodedInfo rdn; - - /* Always points to last rdn tag. */ - const DERByte *rdnTag = rdnSeq.nextItem; - /* Offset relative to base of current rdn set tag. */ - CFIndex rdnTagLocation = 0; - while ((drtn = DERDecodeSeqNext(&rdnSeq, &rdn)) == DR_Success) { - require_quiet(rdn.tag == ASN1_CONSTR_SET, badDER); - /* We don't allow empty RDNs. */ - require_quiet(rdn.content.length != 0, badDER); - /* Length of the tag and length of the current rdn. */ - CFIndex rdnTLLength = rdn.content.data - rdnTag; - CFIndex rdnContentLength = rdn.content.length; - /* Copy the tag and length of the RDN. */ - memcpy(base + rdnTagLocation, rdnTag, rdnTLLength); - - DERSequence atvSeq; - drtn = DERDecodeSeqContentInit(&rdn.content, &atvSeq); - DERDecodedInfo atv; - /* Always points to tag of current atv sequence. */ - const DERByte *atvTag = atvSeq.nextItem; - /* Offset relative to base of current atv sequence tag. */ - CFIndex atvTagLocation = rdnTagLocation + rdnTLLength; - while ((drtn = DERDecodeSeqNext(&atvSeq, &atv)) == DR_Success) { - require_quiet(atv.tag == ASN1_CONSTR_SEQUENCE, badDER); - /* Length of the tag and length of the current atv. */ - CFIndex atvTLLength = atv.content.data - atvTag; - CFIndex atvContentLength = atv.content.length; - /* Copy the tag and length of the atv and the atv itself. */ - memcpy(base + atvTagLocation, atvTag, - atvTLLength + atv.content.length); - - /* Now decode the atv sequence. */ - DERAttributeTypeAndValue atvPair; - drtn = DERParseSequenceContent(&atv.content, - DERNumAttributeTypeAndValueItemSpecs, - DERAttributeTypeAndValueItemSpecs, - &atvPair, sizeof(atvPair)); - require_noerr_quiet(drtn, badDER); - require_quiet(atvPair.type.length != 0, badDER); - DERDecodedInfo value; - drtn = DERDecodeItem(&atvPair.value, &value); - require_noerr_quiet(drtn, badDER); - - /* (c) attribute values in PrintableString are not case sensitive - (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and - - (d) attribute values in PrintableString are compared after - removing leading and trailing white space and converting internal - substrings of one or more consecutive white space characters to a - single space. */ - if (value.tag == ASN1_PRINTABLE_STRING) { - /* Offset relative to base of current value tag. */ - CFIndex valueTagLocation = atvTagLocation + atvPair.value.data - atvTag; - CFIndex valueTLLength = value.content.data - atvPair.value.data; - CFIndex valueContentLength = value.content.length; - - /* Now copy all the bytes, but convert to upper case while - doing so and convert multiple whitespace chars into a - single space. */ - bool lastWasBlank = false; - CFIndex valueLocation = valueTagLocation + valueTLLength; - CFIndex valueCurrentLocation = valueLocation; - CFIndex ix; - for (ix = 0; ix < valueContentLength; ++ix) { - UInt8 ch = value.content.data[ix]; - if (isblank(ch)) { - if (lastWasBlank) { - continue; - } else { - /* Don't insert a space for first character - we encounter. */ - if (valueCurrentLocation > valueLocation) { - base[valueCurrentLocation++] = ' '; - } - lastWasBlank = true; - } - } else { - lastWasBlank = false; - if ('a' <= ch && ch <= 'z') { - base[valueCurrentLocation++] = ch + 'A' - 'a'; - } else { - base[valueCurrentLocation++] = ch; - } - } - } - /* Finally if lastWasBlank remove the trailing space. */ - if (lastWasBlank && valueCurrentLocation > valueLocation) { - valueCurrentLocation--; - } - /* Adjust content length to normalized length. */ - valueContentLength = valueCurrentLocation - valueLocation; - - /* Number of bytes by which the length should be shorted. */ - CFIndex lengthDiff = value.content.length - valueContentLength; - if (lengthDiff == 0) { - /* Easy case no need to adjust lengths. */ - } else { - /* Hard work we need to go back and fix up length fields - for: - 1) The value itself. - 2) The ATV Sequence containing type/value - 3) The RDN Set containing one or more atv pairs. - 4) The result. - */ - - /* Step 1 fix up length of value. */ - /* Length of value tag and length minus the tag. */ - DERSize newValueTLLength = valueTLLength - 1; - drtn = DEREncodeLength(valueContentLength, - base + valueTagLocation + 1, &newValueTLLength); - /* Add the length of the tag back in. */ - newValueTLLength++; - CFIndex valueLLDiff = valueTLLength - newValueTLLength; - if (valueLLDiff) { - /* The size of the length field changed, let's slide - the value back by valueLLDiff bytes. */ - memmove(base + valueTagLocation + newValueTLLength, - base + valueTagLocation + valueTLLength, - valueContentLength); - /* The length diff for the enclosing object. */ - lengthDiff += valueLLDiff; - } - - /* Step 2 fix up length of the enclosing ATV Sequence. */ - atvContentLength -= lengthDiff; - DERSize newATVTLLength = atvTLLength - 1; - drtn = DEREncodeLength(atvContentLength, - base + atvTagLocation + 1, &newATVTLLength); - /* Add the length of the tag back in. */ - newATVTLLength++; - CFIndex atvLLDiff = atvTLLength - newATVTLLength; - if (atvLLDiff) { - /* The size of the length field changed, let's slide - the value back by valueLLDiff bytes. */ - memmove(base + atvTagLocation + newATVTLLength, - base + atvTagLocation + atvTLLength, - atvContentLength); - /* The length diff for the enclosing object. */ - lengthDiff += atvLLDiff; - atvTLLength = newATVTLLength; - } - - /* Step 3 fix up length of enclosing RDN Set. */ - rdnContentLength -= lengthDiff; - DERSize newRDNTLLength = rdnTLLength - 1; - drtn = DEREncodeLength(rdnContentLength, - base + rdnTagLocation + 1, &newRDNTLLength); - /* Add the length of the tag back in. */ - newRDNTLLength++; - CFIndex rdnLLDiff = rdnTLLength - newRDNTLLength; - if (rdnLLDiff) { - /* The size of the length field changed, let's slide - the value back by valueLLDiff bytes. */ - memmove(base + rdnTagLocation + newRDNTLLength, - base + rdnTagLocation + rdnTLLength, - rdnContentLength); - /* The length diff for the enclosing object. */ - lengthDiff += rdnLLDiff; - rdnTLLength = newRDNTLLength; - - /* Adjust the locations that might have changed due to - this slide. */ - atvTagLocation -= rdnLLDiff; - } - } - } - atvTagLocation += atvTLLength + atvContentLength; - atvTag = atvSeq.nextItem; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - rdnTagLocation += rdnTLLength + rdnContentLength; - rdnTag = rdnSeq.nextItem; - } - require_quiet(drtn == DR_EndOfSequence, badDER); - /* Truncate the result to the proper length. */ - CFDataSetLength(result, rdnTagLocation); - - return result; - -badDER: - CFRelease(result); - return NULL; -} - -/* AUDIT[securityd]: - certificate->_der is a caller provided data of any length (might be 0). - - Top level certificate decode. - */ -static bool SecCertificateParse(SecCertificateRefP certificate) -{ - DERReturn drtn; - - check(certificate); - CFAllocatorRef allocator = CFGetAllocator(certificate); - - /* top level decode */ - DERSignedCertCrl signedCert; - drtn = DERParseSequence(&certificate->_der, DERNumSignedCertCrlItemSpecs, - DERSignedCertCrlItemSpecs, &signedCert, - sizeof(signedCert)); - require_noerr_quiet(drtn, badCert); - /* Store tbs since we need to digest it for verification later on. */ - certificate->_tbs = signedCert.tbs; - - /* decode the TBSCert - it was saved in full DER form */ - DERTBSCert tbsCert; - drtn = DERParseSequence(&signedCert.tbs, - DERNumTBSCertItemSpecs, DERTBSCertItemSpecs, - &tbsCert, sizeof(tbsCert)); - require_noerr_quiet(drtn, badCert); - - /* sequence we're given: decode the signedCerts Signature Algorithm. */ - /* This MUST be the same as the certificate->_tbsSigAlg with the exception - of the params field. */ - drtn = DERParseSequenceContent(&signedCert.sigAlg, - DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, - &certificate->_sigAlg, sizeof(certificate->_sigAlg)); - require_noerr_quiet(drtn, badCert); - - /* The contents of signedCert.sig is a bit string whose contents - are the signature itself. */ - DERByte numUnusedBits; - drtn = DERParseBitString(&signedCert.sig, - &certificate->_signature, &numUnusedBits); - require_noerr_quiet(drtn, badCert); - - /* Now decode the tbsCert. */ - - /* First we turn the optional version into an int. */ - if (tbsCert.version.length) { - DERDecodedInfo decoded; - drtn = DERDecodeItem(&tbsCert.version, &decoded); - require_noerr_quiet(drtn, badCert); - require_quiet(decoded.tag == ASN1_INTEGER, badCert); - require_quiet(decoded.content.length == 1, badCert); - certificate->_version = decoded.content.data[0]; - require_quiet(certificate->_version > 0, badCert); - require_quiet(certificate->_version < 3, badCert); - } else { - certificate->_version = 0; - } - - /* The serial number is in the tbsCert.serialNum - it was saved in - INTEGER form without the tag and length. */ - certificate->_serialNum = tbsCert.serialNum; - certificate->_serialNumber = CFDataCreate(allocator, - tbsCert.serialNum.data, tbsCert.serialNum.length); - - /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */ - drtn = DERParseSequenceContent(&tbsCert.tbsSigAlg, - DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, - &certificate->_tbsSigAlg, sizeof(certificate->_tbsSigAlg)); - require_noerr_quiet(drtn, badCert); - - /* The issuer is in the tbsCert.issuer - it's a sequence without the tag - and length fields. */ - certificate->_issuer = tbsCert.issuer; - certificate->_normalizedIssuer = createNormalizedX501Name(allocator, - &tbsCert.issuer); - - /* sequence we're given: decode the tbsCerts Validity sequence. */ - DERValidity validity; - drtn = DERParseSequenceContent(&tbsCert.validity, - DERNumValidityItemSpecs, DERValidityItemSpecs, - &validity, sizeof(validity)); - require_noerr_quiet(drtn, badCert); - require_quiet(derDateGetAbsoluteTime(&validity.notBefore, - &certificate->_notBefore), badCert); - require_quiet(derDateGetAbsoluteTime(&validity.notAfter, - &certificate->_notAfter), badCert); - - /* The subject is in the tbsCert.subject - it's a sequence without the tag - and length fields. */ - certificate->_subject = tbsCert.subject; - certificate->_normalizedSubject = createNormalizedX501Name(allocator, - &tbsCert.subject); - - /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */ - DERSubjPubKeyInfo pubKeyInfo; - drtn = DERParseSequence(&tbsCert.subjectPubKey, - DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs, - &pubKeyInfo, sizeof(pubKeyInfo)); - require_noerr_quiet(drtn, badCert); - - /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */ - drtn = DERParseSequenceContent(&pubKeyInfo.algId, - DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs, - &certificate->_algId, sizeof(certificate->_algId)); - require_noerr_quiet(drtn, badCert); - - /* Now we can figure out the key's algorithm id and params based on - certificate->_algId.oid. */ - - /* The contents of pubKeyInfo.pubKey is a bit string whose contents - are a PKCS1 format RSA key. */ - drtn = DERParseBitString(&pubKeyInfo.pubKey, - &certificate->_pubKeyDER, &numUnusedBits); - require_noerr_quiet(drtn, badCert); - - /* The contents of tbsCert.issuerID is a bit string. */ - certificate->_issuerUniqueID = tbsCert.issuerID; - - /* The contents of tbsCert.subjectID is a bit string. */ - certificate->_subjectUniqueID = tbsCert.subjectID; - - /* Extensions. */ - if (tbsCert.extensions.length) { - CFIndex extensionCount = 0; - DERSequence derSeq; - DERTag tag; - drtn = DERDecodeSeqInit(&tbsCert.extensions, &tag, - &derSeq); - require_noerr_quiet(drtn, badCert); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badCert); - DERDecodedInfo currDecoded; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { -#if 0 -/* ! = MUST recognize ? = SHOULD recognize -*/ - - KnownExtension _subjectKeyID; /* ?SubjectKeyIdentifier id-ce 14 */ - KnownExtension _keyUsage; /* !KeyUsage id-ce 15 */ - KnownExtension _subjectAltName; /* !SubjectAltName id-ce 17 */ - KnownExtension _basicConstraints; /* !BasicConstraints id-ce 19 */ - KnownExtension _authorityKeyID; /* ?AuthorityKeyIdentifier id-ce 35 */ - KnownExtension _extKeyUsage; /* !ExtKeyUsage id-ce 37 */ - KnownExtension _netscapeCertType; /* 2.16.840.1.113730.1.1 netscape 1 1 */ - KnownExtension _qualCertStatements; /* QCStatements id-pe 3 */ - - KnownExtension _issuerAltName; /* IssuerAltName id-ce 18 */ - KnownExtension _nameConstraints; /* !NameConstraints id-ce 30 */ - KnownExtension _cRLDistributionPoints; /* CRLDistributionPoints id-ce 31 */ - KnownExtension _certificatePolicies; /* !CertificatePolicies id-ce 32 */ - KnownExtension _policyMappings; /* ?PolicyMappings id-ce 33 */ - KnownExtension _policyConstraints; /* !PolicyConstraints id-ce 36 */ - KnownExtension _freshestCRL; /* FreshestCRL id-ce 46 */ - KnownExtension _inhibitAnyPolicy; /* !InhibitAnyPolicy id-ce 54 */ - - KnownExtension _authorityInfoAccess; /* AuthorityInfoAccess id-pe 1 */ - KnownExtension _subjectInfoAccess; /* SubjectInfoAccess id-pe 11 */ -#endif - - extensionCount++; - } - require_quiet(drtn == DR_EndOfSequence, badCert); - - /* Put some upper limit on the number of extentions allowed. */ - require_quiet(extensionCount < 10000, badCert); - certificate->_extensionCount = extensionCount; - CFIndex mallocCount = extensionCount > 0 ? extensionCount : 1; - certificate->_extensions = - malloc(sizeof(SecCertificateExtension) * mallocCount); - require_quiet(certificate->_extensions, badCert); - - CFIndex ix = 0; - drtn = DERDecodeSeqInit(&tbsCert.extensions, &tag, &derSeq); - require_noerr_quiet(drtn, badCert); - for (ix = 0; ix < extensionCount; ++ix) { - drtn = DERDecodeSeqNext(&derSeq, &currDecoded); - require_quiet(drtn == DR_Success || - (ix == extensionCount - 1 && drtn == DR_EndOfSequence), badCert); - require_quiet(currDecoded.tag == ASN1_CONSTR_SEQUENCE, badCert); - DERExtension extn; - drtn = DERParseSequenceContent(&currDecoded.content, - DERNumExtensionItemSpecs, DERExtensionItemSpecs, - &extn, sizeof(extn)); - require_noerr_quiet(drtn, badCert); - /* Copy stuff into certificate->extensions[ix]. */ - certificate->_extensions[ix].extnID = extn.extnID; - require_noerr_quiet(drtn = DERParseBooleanWithDefault(&extn.critical, false, - &certificate->_extensions[ix].critical), badCert); - certificate->_extensions[ix].extnValue = extn.extnValue; - - SecCertificateExtensionParser parser = - (SecCertificateExtensionParser)CFDictionaryGetValue( - gExtensionParsers, &certificate->_extensions[ix].extnID); - if (parser) { - /* Invoke the parser. */ - parser(certificate, &certificate->_extensions[ix]); - } else if (certificate->_extensions[ix].critical) { - secinfo("cert", "Found unknown critical extension"); - certificate->_foundUnknownCriticalExtension = true; - } else { - secinfo("cert", "Found unknown non critical extension"); - } - } - } - - return true; - -badCert: - return false; -} - - -/* Public API functions. */ -CFTypeID SecCertificateGetTypeIDP(void) { - pthread_once(&kSecCertificateRegisterClass, SecCertificateRegisterClass); - return kSecCertificateTypeID; -} - -SecCertificateRefP SecCertificateCreateWithBytesP(CFAllocatorRef allocator, - const UInt8 *der_bytes, CFIndex der_length) { - check(der_bytes); - check(der_length); - CFIndex size = sizeof(struct __SecCertificate) + der_length; - SecCertificateRefP result = (SecCertificateRefP)_CFRuntimeCreateInstance( - allocator, SecCertificateGetTypeIDP(), size - sizeof(CFRuntimeBase), 0); - if (result) { - memset((char*)result + sizeof(result->_base), 0, - sizeof(*result) - sizeof(result->_base)); - result->_der.data = ((DERByte *)result + sizeof(*result)); - result->_der.length = der_length; - if(der_bytes) { - memcpy(result->_der.data, der_bytes, der_length); - } - if (!SecCertificateParse(result)) { - CFRelease(result); - return NULL; - } - } - return result; -} - -/* @@@ Placeholder until iap submits a binary is fixed. */ -SecCertificateRefP SecCertificateCreate(CFAllocatorRef allocator, - const UInt8 *der_bytes, CFIndex der_length); - -SecCertificateRefP SecCertificateCreate(CFAllocatorRef allocator, - const UInt8 *der_bytes, CFIndex der_length) { - return SecCertificateCreateWithBytesP(allocator, der_bytes, der_length); -} -/* @@@ End of placeholder. */ - -/* AUDIT[securityd](done): - der_certificate is a caller provided data of any length (might be 0), only - its cf type has been checked. - */ -SecCertificateRefP SecCertificateCreateWithDataP(CFAllocatorRef allocator, - CFDataRef der_certificate) { - check(der_certificate); - CFIndex size = sizeof(struct __SecCertificate); - SecCertificateRefP result = (SecCertificateRefP)_CFRuntimeCreateInstance( - allocator, SecCertificateGetTypeIDP(), size - sizeof(CFRuntimeBase), 0); - if (result) { - memset((char*)result + sizeof(result->_base), 0, size - sizeof(result->_base)); - result->_der_data = CFDataCreateCopy(allocator, der_certificate); - result->_der.data = (DERByte *)CFDataGetBytePtr(result->_der_data); - result->_der.length = CFDataGetLength(result->_der_data); - if (!SecCertificateParse(result)) { - CFRelease(result); - return NULL; - } - } - return result; -} - -CFDataRef SecCertificateCopyDataP(SecCertificateRefP certificate) { - check(certificate); - CFDataRef result; - if (certificate->_der_data) { - CFRetain(certificate->_der_data); - result = certificate->_der_data; - } else { - result = CFDataCreate(CFGetAllocator(certificate), - certificate->_der.data, certificate->_der.length); -#if 0 - /* FIXME: If we wish to cache result we need to lock the certificate. - Also this create 2 copies of the certificate data which is somewhat - suboptimal. */ - CFRetain(result); - certificate->_der_data = result; -#endif - } - - return result; -} - -CFIndex SecCertificateGetLengthP(SecCertificateRefP certificate) { - return certificate->_der.length; -} - -const UInt8 *SecCertificateGetBytePtrP(SecCertificateRefP certificate) { - return certificate->_der.data; -} - -/* From rfc3280 - Appendix B. ASN.1 Notes - - Object Identifiers (OIDs) are used throughout this specification to - identify certificate policies, public key and signature algorithms, - certificate extensions, etc. There is no maximum size for OIDs. - This specification mandates support for OIDs which have arc elements - with values that are less than 2^28, that is, they MUST be between 0 - and 268,435,455, inclusive. This allows each arc element to be - represented within a single 32 bit word. Implementations MUST also - support OIDs where the length of the dotted decimal (see [RFC 2252], - section 4.1) string representation can be up to 100 bytes - (inclusive). Implementations MUST be able to handle OIDs with up to - 20 elements (inclusive). CAs SHOULD NOT issue certificates which - contain OIDs that exceed these requirements. Likewise, CRL issuers - SHOULD NOT issue CRLs which contain OIDs that exceed these - requirements. -*/ - -/* Oids longer than this are considered invalid. */ -#define MAX_OID_SIZE 32 - -static CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator, - const DERItem *oid) { - - if (oid->length == 0) { - return SecFrameworkCopyLocalizedString(CFSTR(""), - CFSTR("SecCertificate")); - } - if (oid->length > MAX_OID_SIZE) { - return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"), - CFSTR("SecCertificate")); - } - - CFMutableStringRef result = CFStringCreateMutable(allocator, 0); - - // The first two levels are encoded into one byte, since the root level - // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then - // y may be > 39, so we have to add special-case handling for this. - uint32_t x = oid->data[0] / 40; - uint32_t y = oid->data[0] % 40; - if (x > 2) - { - // Handle special case for large y if x = 2 - y += (x - 2) * 40; - x = 2; - } - CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y); - - uint32_t value = 0; - for (x = 1; x < oid->length; ++x) - { - value = (value << 7) | (oid->data[x] & 0x7F); - /* @@@ value may not span more than 4 bytes. */ - /* A max number of 20 values is allowed. */ - if (!(oid->data[x] & 0x80)) - { - CFStringAppendFormat(result, NULL, CFSTR(".%lu"), (unsigned long)value); - value = 0; - } - } - return result; -} - -static CFStringRef copyLocalizedOidDescription(CFAllocatorRef allocator, - const DERItem *oid) { - if (oid->length == 0) { - return SecFrameworkCopyLocalizedString(CFSTR(""), - CFSTR("SecCertificate")); - } - - /* Build the key we use to lookup the localized OID description. */ - CFMutableStringRef oidKey = CFStringCreateMutable(allocator, - oid->length * 3 + 5); - CFStringAppendFormat(oidKey, NULL, CFSTR("06 %02lX"), (unsigned long)oid->length); - DERSize ix; - for (ix = 0; ix < oid->length; ++ix) - CFStringAppendFormat(oidKey, NULL, CFSTR(" %02X"), oid->data[ix]); - - CFStringRef name = SecFrameworkCopyLocalizedString(oidKey, CFSTR("OID")); - if (CFEqual(oidKey, name)) { - CFRelease(name); - name = SecDERItemCopyOIDDecimalRepresentation(allocator, oid); - } - CFRelease(oidKey); - - return name; -} - -/* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated - 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't - have a length of exactly 4 or 16 octects. */ -static CFStringRef copyIPAddressContentDescription(CFAllocatorRef allocator, - const DERItem *ip) { - /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's - 4 octects addr, or 8 octects, addr/mask for ipv6 it's - 16 octects addr, or 32 octects addr/mask. */ - CFStringRef value = NULL; - if (ip->length == 4) { - value = CFStringCreateWithFormat(allocator, NULL, - CFSTR("%u.%u.%u.%u"), - ip->data[0], ip->data[1], ip->data[2], ip->data[3]); - } else if (ip->length == 16) { - value = CFStringCreateWithFormat(allocator, NULL, - CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:" - "%02x%02x:%02x%02x:%02x%02x:%02x%02x"), - ip->data[0], ip->data[1], ip->data[2], ip->data[3], - ip->data[4], ip->data[5], ip->data[6], ip->data[7], - ip->data[8], ip->data[9], ip->data[10], ip->data[11], - ip->data[12], ip->data[13], ip->data[14], ip->data[15]); - } - - return value; -} - -#if 0 -static CFStringRef copyFullOidDescription(CFAllocatorRef allocator, - const DERItem *oid) { - CFStringRef decimal = SecDERItemCopyOIDDecimalRepresentation(allocator, oid); - CFStringRef name = copyLocalizedOidDescription(allocator, oid); - CFStringRef oid_string = CFStringCreateWithFormat(allocator, NULL, - CFSTR("%@ (%@)"), name, decimal); - CFRelease(name); - CFRelease(decimal); - return oid_string; -} -#endif - -void appendPropertyP(CFMutableArrayRef properties, - CFStringRef propertyType, CFStringRef label, CFTypeRef value) { - CFDictionaryRef property; - if (label) { - CFStringRef localizedLabel = SecFrameworkCopyLocalizedString(label, - CFSTR("SecCertificate")); - const void *all_keys[4]; - all_keys[0] = kSecPropertyKeyType; - all_keys[1] = kSecPropertyKeyLabel; - all_keys[2] = kSecPropertyKeyLocalizedLabel; - all_keys[3] = kSecPropertyKeyValue; - const void *property_values[] = { - propertyType, - label, - localizedLabel, - value, - }; - property = CFDictionaryCreate(CFGetAllocator(properties), - all_keys, property_values, value ? 4 : 3, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(localizedLabel); - } else { - const void *nolabel_keys[2]; - nolabel_keys[0] = kSecPropertyKeyType; - nolabel_keys[1] = kSecPropertyKeyValue; - const void *property_values[] = { - propertyType, - value, - }; - property = CFDictionaryCreate(CFGetAllocator(properties), - nolabel_keys, property_values, 2, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - } - - CFArrayAppendValue(properties, property); - CFRelease(property); -} - -/* YYMMDDhhmmZ */ -#define UTC_TIME_NOSEC_ZULU_LEN 11 -/* YYMMDDhhmmssZ */ -#define UTC_TIME_ZULU_LEN 13 -/* YYMMDDhhmmssThhmm */ -#define UTC_TIME_LOCALIZED_LEN 17 -/* YYYYMMDDhhmmssZ */ -#define GENERALIZED_TIME_ZULU_LEN 15 -/* YYYYMMDDhhmmssThhmm */ -#define GENERALIZED_TIME_LOCALIZED_LEN 19 - -/* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also - advance *p by 2. */ -static inline SInt32 parseDecimalPair(const DERByte **p) { - const DERByte *cp = *p; - *p += 2; - return 10 * (cp[0] - '0') + cp[1] - '0'; -} - -/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return - true if the date was valid and properly decoded, also return the result in - absTime. Return false otherwise. */ -static CFAbsoluteTime SecAbsoluteTimeFromDateContent(DERTag tag, const uint8_t *bytes, - size_t length) { - check(bytes); - if (length == 0) - return NULL_TIME; - - bool isUtcLength = false; - bool isLocalized = false; - bool noSeconds = false; - switch (length) { - case UTC_TIME_NOSEC_ZULU_LEN: /* YYMMDDhhmmZ */ - isUtcLength = true; - noSeconds = true; - break; - case UTC_TIME_ZULU_LEN: /* YYMMDDhhmmssZ */ - isUtcLength = true; - break; - case GENERALIZED_TIME_ZULU_LEN: /* YYYYMMDDhhmmssZ */ - break; - case UTC_TIME_LOCALIZED_LEN: /* YYMMDDhhmmssThhmm (where T=[+,-]) */ - isUtcLength = true; - /*DROPTHROUGH*/ - case GENERALIZED_TIME_LOCALIZED_LEN:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */ - isLocalized = true; - break; - default: /* unknown format */ - return NULL_TIME; - } - - /* Make sure the der tag fits the thing inside it. */ - if (tag == ASN1_UTC_TIME) { - if (!isUtcLength) - return NULL_TIME; - } else if (tag == ASN1_GENERALIZED_TIME) { - if (isUtcLength) - return NULL_TIME; - } else { - return NULL_TIME; - } - - const DERByte *cp = bytes; - /* Check that all characters are digits, except if localized the timezone - indicator or if not localized the 'Z' at the end. */ - DERSize ix; - for (ix = 0; ix < length; ++ix) { - if (!(isdigit(cp[ix]))) { - if ((isLocalized && ix == length - 5 && - (cp[ix] == '+' || cp[ix] == '-')) || - (!isLocalized && ix == length - 1 && cp[ix] == 'Z')) { - continue; - } - return NULL_TIME; - } - } - - /* Initialize the fields in a gregorian date struct. */ - CFGregorianDate gdate; - if (isUtcLength) { - SInt32 year = parseDecimalPair(&cp); - if (year < 50) { - /* 0 <= year < 50 : assume century 21 */ - gdate.year = 2000 + year; - } else if (year < 70) { - /* 50 <= year < 70 : illegal per PKIX */ - return false; - } else { - /* 70 < year <= 99 : assume century 20 */ - gdate.year = 1900 + year; - } - } else { - gdate.year = 100 * parseDecimalPair(&cp) + parseDecimalPair(&cp); - } - gdate.month = parseDecimalPair(&cp); - gdate.day = parseDecimalPair(&cp); - gdate.hour = parseDecimalPair(&cp); - gdate.minute = parseDecimalPair(&cp); - if (noSeconds) { - gdate.second = 0; - } else { - gdate.second = parseDecimalPair(&cp); - } - - CFTimeInterval timeZoneOffset = 0; - if (isLocalized) { - /* ZONE INDICATOR */ - SInt32 multiplier = *cp++ == '+' ? 60 : -60; - timeZoneOffset = multiplier * - (parseDecimalPair(&cp) + 60 * parseDecimalPair(&cp)); - } else { - timeZoneOffset = 0; - } - - secinfo("dateparse", - "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f", - (int)length, bytes, (int)gdate.year, gdate.month, - gdate.day, gdate.hour, gdate.minute, gdate.second, - timeZoneOffset / 60); - - if (!CFGregorianDateIsValid(gdate, kCFGregorianAllUnits)) - return false; - CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, - timeZoneOffset); - if (!timeZone) - return NULL_TIME; - CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(gdate, timeZone); - CFRelease(timeZone); - return absTime; -} - -static bool derDateContentGetAbsoluteTime(DERTag tag, const DERItem *date, - CFAbsoluteTime *pabsTime) { - CFAbsoluteTime absTime = SecAbsoluteTimeFromDateContent(tag, date->data, - date->length); - if (absTime == NULL_TIME) - return false; - - *pabsTime = absTime; - return true; -} - -/* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return - true if the date was valid and properly decoded, also return the result in - absTime. Return false otherwise. */ -static bool derDateGetAbsoluteTime(const DERItem *dateChoice, - CFAbsoluteTime *absTime) { - check(dateChoice); - check(absTime); - if (dateChoice->length == 0) - return false; - - DERDecodedInfo decoded; - if (DERDecodeItem(dateChoice, &decoded)) - return false; - - return derDateContentGetAbsoluteTime(decoded.tag, &decoded.content, - absTime); -} - -static void appendDataProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *der_data) { - CFDataRef data = CFDataCreate(CFGetAllocator(properties), - der_data->data, der_data->length); - appendPropertyP(properties, kSecPropertyTypeData, label, data); - CFRelease(data); -} - -static void appendUnparsedProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *der_data) { - CFStringRef newLabel = CFStringCreateWithFormat(CFGetAllocator(properties), - NULL, CFSTR("Unparsed %@"), label); - appendDataProperty(properties, newLabel, der_data); - CFRelease(newLabel); -} - -static void appendInvalidProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *der_data) { - CFStringRef newLabel = CFStringCreateWithFormat(CFGetAllocator(properties), - NULL, CFSTR("Invalid %@"), label); - appendDataProperty(properties, newLabel, der_data); - CFRelease(newLabel); -} - -static void appendDateContentProperty(CFMutableArrayRef properties, - CFStringRef label, DERTag tag, const DERItem *dateContent) { - CFAbsoluteTime absTime; - if (!derDateContentGetAbsoluteTime(tag, dateContent, &absTime)) { - /* Date decode failure insert hex bytes instead. */ - return appendInvalidProperty(properties, label, dateContent); - } - CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime); - appendPropertyP(properties, kSecPropertyTypeDate, label, date); - CFRelease(date); -} - -static void appendDateProperty(CFMutableArrayRef properties, - CFStringRef label, CFAbsoluteTime absTime) { - CFDateRef date = CFDateCreate(CFGetAllocator(properties), absTime); - appendPropertyP(properties, kSecPropertyTypeDate, label, date); - CFRelease(date); -} - -static void appendIPAddressContentProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *ip) { - CFStringRef value = - copyIPAddressContentDescription(CFGetAllocator(properties), ip); - if (value) { - appendPropertyP(properties, kSecPropertyTypeString, label, value); - CFRelease(value); - } else { - appendUnparsedProperty(properties, label, ip); - } -} - -static void appendURLContentProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *urlContent) { - CFURLRef url = CFURLCreateWithBytes(CFGetAllocator(properties), - urlContent->data, urlContent->length, kCFStringEncodingASCII, NULL); - if (url) { - appendPropertyP(properties, kSecPropertyTypeURL, label, url); - CFRelease(url); - } else { - appendInvalidProperty(properties, label, urlContent); - } -} - -static void appendURLProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *url) { - DERDecodedInfo decoded; - DERReturn drtn; - - drtn = DERDecodeItem(url, &decoded); - if (drtn || decoded.tag != ASN1_IA5_STRING) { - appendInvalidProperty(properties, label, url); - } else { - appendURLContentProperty(properties, label, &decoded.content); - } -} - -static void appendOIDProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *oid) { - CFStringRef oid_string = copyLocalizedOidDescription(CFGetAllocator(properties), - oid); - appendPropertyP(properties, kSecPropertyTypeString, label, oid_string); - CFRelease(oid_string); -} - -static void appendAlgorithmProperty(CFMutableArrayRef properties, - CFStringRef label, const DERAlgorithmId *algorithm) { - CFMutableArrayRef alg_props = - CFArrayCreateMutable(CFGetAllocator(properties), 0, - &kCFTypeArrayCallBacks); - appendOIDProperty(alg_props, CFSTR("Algorithm"), &algorithm->oid); - if (algorithm->params.length) { - if (algorithm->params.length == 2 && - algorithm->params.data[0] == ASN1_NULL && - algorithm->params.data[1] == 0) { - /* @@@ Localize or perhaps skip it? */ - appendPropertyP(alg_props, kSecPropertyTypeString, - CFSTR("Parameters"), CFSTR("none")); - } else { - appendUnparsedProperty(alg_props, CFSTR("Parameters"), - &algorithm->params); - } - } - appendPropertyP(properties, kSecPropertyTypeSection, label, alg_props); - CFRelease(alg_props); -} - -static CFStringRef copyHexDescription(CFAllocatorRef allocator, - const DERItem *blob) { - CFIndex ix, length = blob->length /* < 24 ? blob->length : 24 */; - CFMutableStringRef string = CFStringCreateMutable(allocator, - blob->length * 3 - 1); - for (ix = 0; ix < length; ++ix) - if (ix == 0) - CFStringAppendFormat(string, NULL, CFSTR("%02X"), blob->data[ix]); - else - CFStringAppendFormat(string, NULL, CFSTR(" %02X"), blob->data[ix]); - - return string; -} - -static CFStringRef copyBlobString(CFAllocatorRef allocator, - CFStringRef blobType, CFStringRef quanta, const DERItem *blob) { - CFStringRef blobFormat = SecFrameworkCopyLocalizedString( - CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate") - /*, "format string for encoded field data (e.g. Sequence; 128 bytes; " - "data = 00 00 ...)" */); - CFStringRef hex = copyHexDescription(allocator, blob); - CFStringRef result = CFStringCreateWithFormat(allocator, NULL, - blobFormat, blobType, blob->length, quanta, hex); - CFRelease(hex); - CFRelease(blobFormat); - - return result; -} - -static CFStringRef copyContentString(CFAllocatorRef allocator, - const DERItem *string, CFStringEncoding encoding, - bool printableOnly) { - /* Strip potential bogus trailing zero from printable strings. */ - DERSize length = string->length; - if (length && string->data[length - 1] == 0) { - /* Don't mess with the length of UTF16 strings though. */ - if (encoding != kCFStringEncodingUTF16) - length--; - } - /* A zero length string isn't considered printable. */ - if (!length && printableOnly) - return NULL; - - /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes - it treat kCFStringEncodingUTF16 as big endian by default, whereas - passing false makes it treat it as native endian by default. */ - CFStringRef result = CFStringCreateWithBytes(allocator, string->data, - length, encoding, encoding == kCFStringEncodingUTF16); - if (result) - return result; - - return printableOnly ? NULL : copyHexDescription(allocator, string); -} - -/* From rfc3280 - Appendix B. ASN.1 Notes - - CAs MUST force the serialNumber to be a non-negative integer, that - is, the sign bit in the DER encoding of the INTEGER value MUST be - zero - this can be done by adding a leading (leftmost) `00'H octet if - necessary. This removes a potential ambiguity in mapping between a - string of octets and an integer value. - - As noted in section 4.1.2.2, serial numbers can be expected to - contain long integers. Certificate users MUST be able to handle - serialNumber values up to 20 octets in length. Conformant CAs MUST - NOT use serialNumber values longer than 20 octets. -*/ - -/* Return the given numeric data as a string: decimal up to 64 bits, - hex otherwise. */ -static CFStringRef copyIntegerContentDescription(CFAllocatorRef allocator, - const DERItem *integer) { - uint64_t value = 0; - CFIndex ix, length = integer->length; - - if (length == 0 || length > 8) - return copyHexDescription(allocator, integer); - - for(ix = 0; ix < length; ++ix) { - value <<= 8; - value += integer->data[ix]; - } - - return CFStringCreateWithFormat(allocator, NULL, CFSTR("%llu"), value); -} - -static CFStringRef copyDERThingContentDescription(CFAllocatorRef allocator, - DERTag tag, const DERItem *derThing, bool printableOnly) { - switch(tag) { - case ASN1_INTEGER: - case ASN1_BOOLEAN: - return printableOnly ? NULL : copyIntegerContentDescription(allocator, derThing); - case ASN1_PRINTABLE_STRING: - case ASN1_IA5_STRING: - return copyContentString(allocator, derThing, kCFStringEncodingASCII, printableOnly); - case ASN1_UTF8_STRING: - case ASN1_GENERAL_STRING: - case ASN1_UNIVERSAL_STRING: - return copyContentString(allocator, derThing, kCFStringEncodingUTF8, printableOnly); - case ASN1_T61_STRING: // 20, also BER_TAG_TELETEX_STRING - case ASN1_VIDEOTEX_STRING: // 21 - case ASN1_VISIBLE_STRING: // 26 - return copyContentString(allocator, derThing, kCFStringEncodingISOLatin1, printableOnly); - case ASN1_BMP_STRING: // 30 - return copyContentString(allocator, derThing, kCFStringEncodingUTF16, printableOnly); - case ASN1_OCTET_STRING: - return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Byte string"), CFSTR("bytes"), - derThing); - //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing); - case ASN1_BIT_STRING: - return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Bit string"), CFSTR("bits"), - derThing); - case (DERByte)ASN1_CONSTR_SEQUENCE: - return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Sequence"), CFSTR("bytes"), - derThing); - case (DERByte)ASN1_CONSTR_SET: - return printableOnly ? NULL : copyBlobString(allocator, CFSTR("Set"), CFSTR("bytes"), - derThing); - case ASN1_OBJECT_ID: - return printableOnly ? NULL : copyLocalizedOidDescription(allocator, derThing); - default: - /* @@@ Localize. */ - /* "format string for undisplayed field data with a given DER tag" */ - return printableOnly ? NULL : CFStringCreateWithFormat(allocator, NULL, - CFSTR("not displayed (tag = %llu; length %d)"), - tag, (int)derThing->length); - } -} - -static CFStringRef copyDERThingDescription(CFAllocatorRef allocator, - const DERItem *derThing, bool printableOnly) { - DERDecodedInfo decoded; - DERReturn drtn; - - drtn = DERDecodeItem(derThing, &decoded); - if (drtn) { - return printableOnly ? NULL : copyHexDescription(allocator, derThing); - } else { - return copyDERThingContentDescription(allocator, decoded.tag, - &decoded.content, false); - } -} - -static void appendDERThingProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *derThing) { - CFStringRef value = copyDERThingDescription(CFGetAllocator(properties), - derThing, false); - appendPropertyP(properties, kSecPropertyTypeString, label, value); - CFReleaseSafe(value); -} - -static OSStatus appendRDNProperty(void *context, const DERItem *rdnType, - const DERItem *rdnValue, CFIndex rdnIX) { - CFMutableArrayRef properties = (CFMutableArrayRef)context; - if (rdnIX > 0) { - /* If there is more than one value pair we create a subsection for the - second pair, and append things to the subsection for subsequent - pairs. */ - CFIndex lastIX = CFArrayGetCount(properties) - 1; - CFTypeRef lastValue = CFArrayGetValueAtIndex(properties, lastIX); - if (rdnIX == 1) { - /* Since this is the second rdn pair for a given rdn, we setup a - new subsection for this rdn. We remove the first property - from the properties array and make it the first element in the - subsection instead. */ - CFMutableArrayRef rdn_props = CFArrayCreateMutable( - CFGetAllocator(properties), 0, &kCFTypeArrayCallBacks); - CFArrayAppendValue(rdn_props, lastValue); - CFArrayRemoveValueAtIndex(properties, lastIX); - appendPropertyP(properties, kSecPropertyTypeSection, NULL, rdn_props); - properties = rdn_props; - } else { - /* Since this is the third or later rdn pair we have already - created a subsection in the top level properties array. Instead - of appending to that directly we append to the array inside the - subsection. */ - properties = (CFMutableArrayRef)CFDictionaryGetValue( - (CFDictionaryRef)lastValue, kSecPropertyKeyValue); - } - } - - /* Finally we append the new rdn value to the property array. */ - CFStringRef label = copyLocalizedOidDescription(CFGetAllocator(properties), - rdnType); - if (label) { - appendDERThingProperty(properties, label, rdnValue); - CFRelease(label); - return errSecSuccess; - } else { - return errSecInvalidCertificate; - } -} - -static CFArrayRef createPropertiesForRDNContent(CFAllocatorRef allocator, - const DERItem *rdnSetContent) { - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - OSStatus status = parseRDNContent(rdnSetContent, properties, - appendRDNProperty); - if (status) { - CFArrayRemoveAllValues(properties); - appendInvalidProperty(properties, CFSTR("RDN"), rdnSetContent); - } - - return properties; -} - -/* - From rfc3739 - 3.1.2. Subject - - When parsing the subject here are some tips for a short name of the cert. - Choice I: commonName - Choice II: givenName - Choice III: pseudonym - - The commonName attribute value SHALL, when present, contain a name - of the subject. This MAY be in the subject's preferred - presentation format, or a format preferred by the CA, or some - other format. Pseudonyms, nicknames, and names with spelling - other than defined by the registered name MAY be used. To - understand the nature of the name presented in commonName, - complying applications MAY have to examine present values of the - givenName and surname attributes, or the pseudonym attribute. - -*/ -static CFArrayRef createPropertiesForX501NameContent(CFAllocatorRef allocator, - const DERItem *x501NameContent) { - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - OSStatus status = parseX501NameContent(x501NameContent, properties, - appendRDNProperty); - if (status) { - CFArrayRemoveAllValues(properties); - appendInvalidProperty(properties, CFSTR("X.501 Name"), x501NameContent); - } - - return properties; -} - -static CFArrayRef createPropertiesForX501Name(CFAllocatorRef allocator, - const DERItem *x501Name) { - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - OSStatus status = parseX501Name(x501Name, properties, appendRDNProperty); - if (status) { - CFArrayRemoveAllValues(properties); - appendInvalidProperty(properties, CFSTR("X.501 Name"), x501Name); - } - - return properties; -} - -static void appendIntegerProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *integer) { - CFStringRef string = copyIntegerContentDescription( - CFGetAllocator(properties), integer); - appendPropertyP(properties, kSecPropertyTypeString, label, string); - CFRelease(string); -} - -static void appendBoolProperty(CFMutableArrayRef properties, - CFStringRef label, bool boolean) { - appendPropertyP(properties, kSecPropertyTypeString, - label, boolean ? CFSTR("Yes") : CFSTR("No")); -} - -static void appendBooleanProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *boolean, bool defaultValue) { - bool result; - DERReturn drtn = DERParseBooleanWithDefault(boolean, defaultValue, &result); - if (drtn) { - /* Couldn't parse boolean; dump the raw unparsed data as hex. */ - appendInvalidProperty(properties, label, boolean); - } else { - appendBoolProperty(properties, label, result); - } -} - -static void appendBitStringContentNames(CFMutableArrayRef properties, - CFStringRef label, const DERItem *bitStringContent, - const CFStringRef *names, CFIndex namesCount) { - DERSize len = bitStringContent->length - 1; - require_quiet(len == 1 || len == 2, badDER); - DERByte numUnusedBits = bitStringContent->data[0]; - require_quiet(numUnusedBits < 8, badDER); - uint_fast16_t bits = 8 * len - numUnusedBits; - require_quiet(bits <= (uint_fast16_t)namesCount, badDER); - uint_fast16_t value = bitStringContent->data[1]; - uint_fast16_t mask; - if (len > 1) { - value = (value << 8) + bitStringContent->data[2]; - mask = 0x8000; - } else { - mask = 0x80; - } - uint_fast16_t ix; - bool didOne = false; - CFMutableStringRef string = - CFStringCreateMutable(CFGetAllocator(properties), 0); - for (ix = 0; ix < bits; ++ix) { - if (value & mask) { - if (didOne) { - CFStringAppend(string, CFSTR(", ")); - } else { - didOne = true; - } - CFStringAppend(string, names[ix]); - } - mask >>= 1; - } - appendPropertyP(properties, kSecPropertyTypeString, label, string); - CFRelease(string); - return; -badDER: - appendInvalidProperty(properties, label, bitStringContent); -} - -static void appendBitStringNames(CFMutableArrayRef properties, - CFStringRef label, const DERItem *bitString, - const CFStringRef *names, CFIndex namesCount) { - DERDecodedInfo bitStringContent; - DERReturn drtn = DERDecodeItem(bitString, &bitStringContent); - require_noerr_quiet(drtn, badDER); - require_quiet(bitStringContent.tag == ASN1_BIT_STRING, badDER); - appendBitStringContentNames(properties, label, &bitStringContent.content, - names, namesCount); - return; -badDER: - appendInvalidProperty(properties, label, bitString); -} - -#if 0 -typedef uint16_t SecKeyUsage; - -#define kSecKeyUsageDigitalSignature 0x8000 -#define kSecKeyUsageNonRepudiation 0x4000 -#define kSecKeyUsageKeyEncipherment 0x2000 -#define kSecKeyUsageDataEncipherment 0x1000 -#define kSecKeyUsageKeyAgreement 0x0800 -#define kSecKeyUsageKeyCertSign 0x0400 -#define kSecKeyUsageCRLSign 0x0200 -#define kSecKeyUsageEncipherOnly 0x0100 -#define kSecKeyUsageDecipherOnly 0x0080 - -/* - KeyUsage ::= BIT STRING { - digitalSignature (0), - nonRepudiation (1), - keyEncipherment (2), - dataEncipherment (3), - keyAgreement (4), - keyCertSign (5), - cRLSign (6), - encipherOnly (7), - decipherOnly (8) } - */ -static void appendKeyUsage(CFMutableArrayRef properties, - const DERItem *extnValue) { - if ((extnValue->length != 4 && extnValue->length != 5) || - extnValue->data[0] != ASN1_BIT_STRING || - extnValue->data[1] < 2 || extnValue->data[1] > 3 || - extnValue->data[2] > 7) { - appendInvalidProperty(properties, CFSTR("KeyUsage Extension"), - extnValue); - } else { - CFMutableStringRef string = - CFStringCreateMutable(CFGetAllocator(properties), 0); - SecKeyUsage usage = (extnValue->data[3] << 8); - if (extnValue->length == 5) - usage += extnValue->data[4]; - secinfo("keyusage", "keyusage: %04X", usage); - static const CFStringRef usageNames[] = { - CFSTR("Digital Signature"), - CFSTR("Non-Repudiation"), - CFSTR("Key Encipherment"), - CFSTR("Data Encipherment"), - CFSTR("Key Agreement"), - CFSTR("Cert Sign"), - CFSTR("CRL Sign"), - CFSTR("Encipher"), - CFSTR("Decipher"), - }; - bool didOne = false; - SecKeyUsage mask = kSecKeyUsageDigitalSignature; - CFIndex ix, bits = (extnValue->data[1] - 1) * 8 - extnValue->data[2]; - for (ix = 0; ix < bits; ++ix) { - if (usage & mask) { - if (didOne) { - CFStringAppend(string, CFSTR(", ")); - } else { - didOne = true; - } - /* @@@ Localize usageNames[ix]. */ - CFStringAppend(string, usageNames[ix]); - } - mask >>= 1; - } - appendPropertyP(properties, kSecPropertyTypeString, CFSTR("Usage"), - string); - CFRelease(string); - } -} -#else -static void appendKeyUsage(CFMutableArrayRef properties, - const DERItem *extnValue) { - static const CFStringRef usageNames[] = { - CFSTR("Digital Signature"), - CFSTR("Non-Repudiation"), - CFSTR("Key Encipherment"), - CFSTR("Data Encipherment"), - CFSTR("Key Agreement"), - CFSTR("Cert Sign"), - CFSTR("CRL Sign"), - CFSTR("Encipher Only"), - CFSTR("Decipher Only") - }; - appendBitStringNames(properties, CFSTR("Usage"), extnValue, - usageNames, sizeof(usageNames) / sizeof(*usageNames)); -} -#endif - -static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERPrivateKeyUsagePeriod pkup; - DERReturn drtn = DERParseSequence(extnValue, - DERNumPrivateKeyUsagePeriodItemSpecs, DERPrivateKeyUsagePeriodItemSpecs, - &pkup, sizeof(pkup)); - require_noerr_quiet(drtn, badDER); - if (pkup.notBefore.length) { - appendDateContentProperty(properties, CFSTR("Not Valid Before"), - ASN1_GENERALIZED_TIME, &pkup.notBefore); - } - if (pkup.notAfter.length) { - appendDateContentProperty(properties, CFSTR("Not Valid After"), - ASN1_GENERALIZED_TIME, &pkup.notAfter); - } - return; -badDER: - appendInvalidProperty(properties, CFSTR("Private Key Usage Period"), - extnValue); -} - -static void appendStringContentProperty(CFMutableArrayRef properties, - CFStringRef label, const DERItem *stringContent, - CFStringEncoding encoding) { - CFStringRef string = CFStringCreateWithBytes(CFGetAllocator(properties), - stringContent->data, stringContent->length, encoding, FALSE); - if (string) { - appendPropertyP(properties, kSecPropertyTypeString, label, string); - CFRelease(string); - } else { - appendInvalidProperty(properties, label, stringContent); - } -} - -/* - OtherName ::= SEQUENCE { - type-id OBJECT IDENTIFIER, - value [0] EXPLICIT ANY DEFINED BY type-id } -*/ -static void appendOtherNameContentProperty(CFMutableArrayRef properties, - const DERItem *otherNameContent) { - DEROtherName on; - DERReturn drtn = DERParseSequenceContent(otherNameContent, - DERNumOtherNameItemSpecs, DEROtherNameItemSpecs, - &on, sizeof(on)); - require_noerr_quiet(drtn, badDER); - CFAllocatorRef allocator = CFGetAllocator(properties); - CFStringRef oid_string = copyLocalizedOidDescription(allocator, - &on.typeIdentifier); - CFStringRef value_string = copyDERThingDescription(allocator, &on.value, false); - if (value_string) - appendPropertyP(properties, kSecPropertyTypeString, oid_string, - value_string); - else - appendUnparsedProperty(properties, oid_string, &on.value); - - CFReleaseNull(value_string); - CFReleaseNull(oid_string); - return; -badDER: - appendInvalidProperty(properties, CFSTR("Other Name"), otherNameContent); -} - -/* - GeneralName ::= CHOICE { - otherName [0] OtherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER} - - EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - */ -static bool appendGeneralNameContentProperty(CFMutableArrayRef properties, - DERTag tag, const DERItem *generalName) { - switch (tag) { - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0: - appendOtherNameContentProperty(properties, generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 1: - /* IA5String. */ - appendStringContentProperty(properties, CFSTR("Email Address"), - generalName, kCFStringEncodingASCII); - break; - case ASN1_CONTEXT_SPECIFIC | 2: - /* IA5String. */ - appendStringContentProperty(properties, CFSTR("DNS Name"), generalName, - kCFStringEncodingASCII); - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3: - appendUnparsedProperty(properties, CFSTR("X.400 Address"), - generalName); - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 4: - { - CFArrayRef directory_plist = - createPropertiesForX501Name(CFGetAllocator(properties), - generalName); - appendPropertyP(properties, kSecPropertyTypeSection, - CFSTR("Directory Name"), directory_plist); - CFRelease(directory_plist); - break; - } - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 5: - appendUnparsedProperty(properties, CFSTR("EDI Party Name"), - generalName); - break; - case ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 6: - /* Technically I don't think this is valid, but there are certs out - in the wild that use a constructed IA5String. In particular the - VeriSign Time Stamping Authority CA.cer does this. */ - appendURLProperty(properties, CFSTR("URI"), generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 6: - appendURLContentProperty(properties, CFSTR("URI"), generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 7: - appendIPAddressContentProperty(properties, CFSTR("IP Address"), - generalName); - break; - case ASN1_CONTEXT_SPECIFIC | 8: - appendOIDProperty(properties, CFSTR("Registered ID"), generalName); - break; - default: - goto badDER; - } - return true; -badDER: - return false; -} - -static void appendGeneralNameProperty(CFMutableArrayRef properties, - const DERItem *generalName) { - DERDecodedInfo generalNameContent; - DERReturn drtn = DERDecodeItem(generalName, &generalNameContent); - require_noerr_quiet(drtn, badDER); - if (appendGeneralNameContentProperty(properties, generalNameContent.tag, - &generalNameContent.content)) - return; -badDER: - appendInvalidProperty(properties, CFSTR("General Name"), generalName); -} - - -/* - GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - */ -static void appendGeneralNamesContent(CFMutableArrayRef properties, - const DERItem *generalNamesContent) { - DERSequence gnSeq; - DERReturn drtn = DERDecodeSeqContentInit(generalNamesContent, &gnSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo generalNameContent; - while ((drtn = DERDecodeSeqNext(&gnSeq, &generalNameContent)) == - DR_Success) { - if (!appendGeneralNameContentProperty(properties, - generalNameContent.tag, &generalNameContent.content)) { - goto badDER; - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, CFSTR("General Names"), - generalNamesContent); -} - -static void appendGeneralNames(CFMutableArrayRef properties, - const DERItem *generalNames) { - DERDecodedInfo generalNamesContent; - DERReturn drtn = DERDecodeItem(generalNames, &generalNamesContent); - require_noerr_quiet(drtn, badDER); - require_quiet(generalNamesContent.tag == ASN1_CONSTR_SEQUENCE, - badDER); - appendGeneralNamesContent(properties, &generalNamesContent.content); - return; -badDER: - appendInvalidProperty(properties, CFSTR("General Names"), generalNames); -} - -/* -BasicConstraints ::= SEQUENCE { - cA BOOLEAN DEFAULT FALSE, - pathLenConstraint INTEGER (0..MAX) OPTIONAL } -*/ -static void appendBasicConstraints(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERBasicConstraints basicConstraints; - DERReturn drtn = DERParseSequence(extnValue, - DERNumBasicConstraintsItemSpecs, DERBasicConstraintsItemSpecs, - &basicConstraints, sizeof(basicConstraints)); - require_noerr_quiet(drtn, badDER); - - appendBooleanProperty(properties, CFSTR("Certificate Authority"), - &basicConstraints.cA, false); - - if (basicConstraints.pathLenConstraint.length != 0) { - appendIntegerProperty(properties, CFSTR("Path Length Constraint"), - &basicConstraints.pathLenConstraint); - } - return; -badDER: - appendInvalidProperty(properties, CFSTR("Basic Constraints"), extnValue); -} - -/* - CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - - DistributionPoint ::= SEQUENCE { - distributionPoint [0] DistributionPointName OPTIONAL, - reasons [1] ReasonFlags OPTIONAL, - cRLIssuer [2] GeneralNames OPTIONAL } - - DistributionPointName ::= CHOICE { - fullName [0] GeneralNames, - nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - - ReasonFlags ::= BIT STRING { - unused (0), - keyCompromise (1), - cACompromise (2), - affiliationChanged (3), - superseded (4), - cessationOfOperation (5), - certificateHold (6), - privilegeWithdrawn (7), - aACompromise (8) } -*/ -static void appendCrlDistributionPoints(CFMutableArrayRef properties, - const DERItem *extnValue) { - CFAllocatorRef allocator = CFGetAllocator(properties); - DERTag tag; - DERSequence dpSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &dpSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo dpSeqContent; - while ((drtn = DERDecodeSeqNext(&dpSeq, &dpSeqContent)) == DR_Success) { - require_quiet(dpSeqContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDistributionPoint dp; - drtn = DERParseSequenceContent(&dpSeqContent.content, - DERNumDistributionPointItemSpecs, - DERDistributionPointItemSpecs, - &dp, sizeof(dp)); - require_noerr_quiet(drtn, badDER); - if (dp.distributionPoint.length) { - DERDecodedInfo distributionPointName; - drtn = DERDecodeItem(&dp.distributionPoint, &distributionPointName); - require_noerr_quiet(drtn, badDER); - if (distributionPointName.tag == - (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0)) { - /* Full Name */ - appendGeneralNamesContent(properties, - &distributionPointName.content); - } else if (distributionPointName.tag == - (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1)) { - CFArrayRef rdn_props = createPropertiesForRDNContent(allocator, - &dp.reasons); - appendPropertyP(properties, kSecPropertyTypeSection, - CFSTR("Name Relative To CRL Issuer"), rdn_props); - CFRelease(rdn_props); - } else { - goto badDER; - } - } - if (dp.reasons.length) { - static const CFStringRef reasonNames[] = { - CFSTR("Unused"), - CFSTR("Key Compromise"), - CFSTR("CA Compromise"), - CFSTR("Affiliation Changed"), - CFSTR("Superseded"), - CFSTR("Cessation Of Operation"), - CFSTR("Certificate Hold"), - CFSTR("Priviledge Withdrawn"), - CFSTR("AA Compromise") - }; - appendBitStringContentNames(properties, CFSTR("Reasons"), - &dp.reasons, - reasonNames, sizeof(reasonNames) / sizeof(*reasonNames)); - } - if (dp.cRLIssuer.length) { - CFMutableArrayRef crlIssuer = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - appendPropertyP(properties, kSecPropertyTypeSection, - CFSTR("CRL Issuer"), crlIssuer); - CFRelease(crlIssuer); - appendGeneralNames(crlIssuer, &dp.cRLIssuer); - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, CFSTR("Crl Distribution Points"), - extnValue); -} - -/* Decode a sequence of integers into a comma separated list of ints. */ -static void appendIntegerSequenceContent(CFMutableArrayRef properties, - CFStringRef label, const DERItem *intSequenceContent) { - CFMutableStringRef value = NULL; - CFStringRef intDesc = NULL; - CFAllocatorRef allocator = CFGetAllocator(properties); - DERSequence intSeq; - DERReturn drtn = DERDecodeSeqContentInit(intSequenceContent, &intSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo intContent; - - while ((drtn = DERDecodeSeqNext(&intSeq, &intContent)) - == DR_Success) { - require_quiet(intContent.tag == ASN1_INTEGER, badDER); - intDesc = copyIntegerContentDescription( - allocator, &intContent.content); - require_quiet(intDesc, badDER); - if (value) { - CFStringAppendFormat(value, NULL, CFSTR(", %@"), intDesc); - } else { - value = CFStringCreateMutableCopy(allocator, 0, intDesc); - require_quiet(value, badDER); - } - CFReleaseNull(intDesc); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - if (value) { - appendPropertyP(properties, kSecPropertyTypeString, - CFSTR("Notice Numbers"), value); - CFRelease(value); - return; - } - /* DROPTHOUGH if !value. */ -badDER: - CFReleaseNull(value); - CFReleaseNull(intDesc); - appendInvalidProperty(properties, label, intSequenceContent); -} - -static void appendCertificatePolicies(CFMutableArrayRef properties, - const DERItem *extnValue) { - CFAllocatorRef allocator = CFGetAllocator(properties); - CFStringRef piLabel = NULL, pqLabel = NULL; - DERTag tag; - DERSequence piSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &piSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo piContent; - int pin = 1; - while ((drtn = DERDecodeSeqNext(&piSeq, &piContent)) == DR_Success) { - require_quiet(piContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERPolicyInformation pi; - drtn = DERParseSequenceContent(&piContent.content, - DERNumPolicyInformationItemSpecs, - DERPolicyInformationItemSpecs, - &pi, sizeof(pi)); - require_noerr_quiet(drtn, badDER); - require_quiet(piLabel = CFStringCreateWithFormat(allocator, NULL, - CFSTR("Policy Identifier #%d"), pin++), badDER); - appendOIDProperty(properties, piLabel, &pi.policyIdentifier); - CFReleaseNull(piLabel); - if (pi.policyQualifiers.length == 0) - continue; - - DERSequence pqSeq; - drtn = DERDecodeSeqContentInit(&pi.policyQualifiers, &pqSeq); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo pqContent; - int pqn = 1; - while ((drtn = DERDecodeSeqNext(&pqSeq, &pqContent)) == DR_Success) { - DERPolicyQualifierInfo pqi; - drtn = DERParseSequenceContent(&pqContent.content, - DERNumPolicyQualifierInfoItemSpecs, - DERPolicyQualifierInfoItemSpecs, - &pqi, sizeof(pqi)); - require_noerr_quiet(drtn, badDER); - DERDecodedInfo qualifierContent; - drtn = DERDecodeItem(&pqi.qualifier, &qualifierContent); - require_noerr_quiet(drtn, badDER); - require_quiet(pqLabel = CFStringCreateWithFormat(allocator, NULL, - CFSTR("Policy Qualifier #%d"), pqn++), badDER); - appendOIDProperty(properties, pqLabel, &pqi.policyQualifierID); - CFReleaseNull(pqLabel); - if (DEROidCompare(&oidQtCps, &pqi.policyQualifierID)) { - require_quiet(qualifierContent.tag == ASN1_IA5_STRING, badDER); - appendURLContentProperty(properties, - CFSTR("CPS URI"), - &qualifierContent.content); - } else if (DEROidCompare(&oidQtUNotice, &pqi.policyQualifierID)) { - require_quiet(qualifierContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERUserNotice un; - drtn = DERParseSequenceContent(&qualifierContent.content, - DERNumUserNoticeItemSpecs, - DERUserNoticeItemSpecs, - &un, sizeof(un)); - require_noerr_quiet(drtn, badDER); - if (un.noticeRef.length) { - DERNoticeReference nr; - drtn = DERParseSequenceContent(&un.noticeRef, - DERNumNoticeReferenceItemSpecs, - DERNoticeReferenceItemSpecs, - &nr, sizeof(nr)); - require_noerr_quiet(drtn, badDER); - appendDERThingProperty(properties, - CFSTR("Organization"), - &nr.organization); - appendIntegerSequenceContent(properties, - CFSTR("Notice Numbers"), &nr.noticeNumbers); - } - if (un.explicitText.length) { - appendDERThingProperty(properties, CFSTR("Explicit Text"), - &un.explicitText); - } - } else { - appendUnparsedProperty(properties, CFSTR("Qualifier"), - &pqi.qualifier); - } - } - require_quiet(drtn == DR_EndOfSequence, badDER); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - CFReleaseNull(piLabel); - CFReleaseNull(pqLabel); - appendInvalidProperty(properties, CFSTR("Certificate Policies"), - extnValue); -} - -static void appendSubjectKeyIdentifier(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERReturn drtn; - DERDecodedInfo keyIdentifier; - drtn = DERDecodeItem(extnValue, &keyIdentifier); - require_noerr_quiet(drtn, badDER); - require_quiet(keyIdentifier.tag == ASN1_OCTET_STRING, badDER); - appendDataProperty(properties, CFSTR("Key Identifier"), - &keyIdentifier.content); - - return; -badDER: - appendInvalidProperty(properties, CFSTR("Invalid Subject Key Identifier"), - extnValue); -} - -/* -AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - -- authorityCertIssuer and authorityCertSerialNumber MUST both - -- be present or both be absent - -KeyIdentifier ::= OCTET STRING -*/ -static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERAuthorityKeyIdentifier akid; - DERReturn drtn; - drtn = DERParseSequence(extnValue, - DERNumAuthorityKeyIdentifierItemSpecs, - DERAuthorityKeyIdentifierItemSpecs, - &akid, sizeof(akid)); - require_noerr_quiet(drtn, badDER); - if (akid.keyIdentifier.length) { - appendDataProperty(properties, CFSTR("Key Identifier"), - &akid.keyIdentifier); - } - if (akid.authorityCertIssuer.length || - akid.authorityCertSerialNumber.length) { - require_quiet(akid.authorityCertIssuer.length && - akid.authorityCertSerialNumber.length, badDER); - /* Perhaps put in a subsection called Authority Certificate Issuer. */ - appendGeneralNamesContent(properties, - &akid.authorityCertIssuer); - appendIntegerProperty(properties, - CFSTR("Authority Certificate Serial Number"), - &akid.authorityCertSerialNumber); - } - - return; -badDER: - appendInvalidProperty(properties, CFSTR("Authority Key Identifier"), - extnValue); -} - -/* - PolicyConstraints ::= SEQUENCE { - requireExplicitPolicy [0] SkipCerts OPTIONAL, - inhibitPolicyMapping [1] SkipCerts OPTIONAL } - - SkipCerts ::= INTEGER (0..MAX) -*/ -static void appendPolicyConstraints(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERPolicyConstraints pc; - DERReturn drtn; - drtn = DERParseSequence(extnValue, - DERNumPolicyConstraintsItemSpecs, - DERPolicyConstraintsItemSpecs, - &pc, sizeof(pc)); - require_noerr_quiet(drtn, badDER); - if (pc.requireExplicitPolicy.length) { - appendIntegerProperty(properties, - CFSTR("Require Explicit Policy"), &pc.requireExplicitPolicy); - } - if (pc.inhibitPolicyMapping.length) { - appendIntegerProperty(properties, - CFSTR("Inhibit Policy Mapping"), &pc.inhibitPolicyMapping); - } - - return; - -badDER: - appendInvalidProperty(properties, CFSTR("Policy Constraints"), extnValue); -} - -/* -extendedKeyUsage EXTENSION ::= { - SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId - IDENTIFIED BY id-ce-extKeyUsage } - -KeyPurposeId ::= OBJECT IDENTIFIER -*/ -static void appendExtendedKeyUsage(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERTag tag; - DERSequence derSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &derSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo currDecoded; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { - require_quiet(currDecoded.tag == ASN1_OBJECT_ID, badDER); - appendOIDProperty(properties, CFSTR("Purpose"), - &currDecoded.content); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, CFSTR("Extended Key Usage"), extnValue); -} - -/* - id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } - - AuthorityInfoAccessSyntax ::= - SEQUENCE SIZE (1..MAX) OF AccessDescription - - AccessDescription ::= SEQUENCE { - accessMethod OBJECT IDENTIFIER, - accessLocation GeneralName } - - id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } - - id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } - - id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } -*/ -static void appendInfoAccess(CFMutableArrayRef properties, - const DERItem *extnValue) { - DERTag tag; - DERSequence adSeq; - DERReturn drtn = DERDecodeSeqInit(extnValue, &tag, &adSeq); - require_noerr_quiet(drtn, badDER); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badDER); - DERDecodedInfo adContent; - while ((drtn = DERDecodeSeqNext(&adSeq, &adContent)) == DR_Success) { - require_quiet(adContent.tag == ASN1_CONSTR_SEQUENCE, badDER); - DERAccessDescription ad; - drtn = DERParseSequenceContent(&adContent.content, - DERNumAccessDescriptionItemSpecs, - DERAccessDescriptionItemSpecs, - &ad, sizeof(ad)); - require_noerr_quiet(drtn, badDER); - appendOIDProperty(properties, CFSTR("Access Method"), - &ad.accessMethod); - //CFSTR("Access Location"); - appendGeneralNameProperty(properties, &ad.accessLocation); - } - require_quiet(drtn == DR_EndOfSequence, badDER); - return; -badDER: - appendInvalidProperty(properties, CFSTR("Authority Information Access"), - extnValue); -} - -static void appendNetscapeCertType(CFMutableArrayRef properties, - const DERItem *extnValue) { - static const CFStringRef certTypes[] = { - CFSTR("SSL client"), - CFSTR("SSL server"), - CFSTR("S/MIME"), - CFSTR("Object Signing"), - CFSTR("Reserved"), - CFSTR("SSL CA"), - CFSTR("S/MIME CA"), - CFSTR("Object Signing CA") - }; - appendBitStringNames(properties, CFSTR("Usage"), extnValue, - certTypes, sizeof(certTypes) / sizeof(*certTypes)); -} - -#if 0 -static void appendEntrustVersInfo(CFMutableArrayRef properties, - const DERItem *extnValue) { -} - -/* - * The list of Qualified Cert Statement statementIds we understand, even though - * we don't actually do anything with them; if these are found in a Qualified - * Cert Statement that's critical, we can truthfully say "yes we understand this". - */ -static const CSSM_OID_PTR knownQualifiedCertStatements[] = -{ - /* id-qcs := { id-pkix 11 } */ - (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V1, /* id-qcs 1 */ - (const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V2, /* id-qcs 2 */ - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_COMPLIANCE, - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE, - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_RETENTION, - (const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_SSCD -}; -#define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR)) -*/ -static void appendQCCertStatements(CFMutableArrayRef properties, - const DERItem *extnValue) { -} - -#endif - -static bool appendPrintableDERSequenceP(CFMutableArrayRef properties, - CFStringRef label, const DERItem *sequence) { - DERTag tag; - DERSequence derSeq; - DERReturn drtn = DERDecodeSeqInit(sequence, &tag, &derSeq); - require_noerr_quiet(drtn, badSequence); - require_quiet(tag == ASN1_CONSTR_SEQUENCE, badSequence); - DERDecodedInfo currDecoded; - bool appendedSomething = false; - while ((drtn = DERDecodeSeqNext(&derSeq, &currDecoded)) == DR_Success) { - switch (currDecoded.tag) - { - case 0: // 0 - case ASN1_SEQUENCE: // 16 - case ASN1_SET: // 17 - // skip constructed object lengths - break; - - case ASN1_UTF8_STRING: // 12 - case ASN1_NUMERIC_STRING: // 18 - case ASN1_PRINTABLE_STRING: // 19 - case ASN1_T61_STRING: // 20, also ASN1_TELETEX_STRING - case ASN1_VIDEOTEX_STRING: // 21 - case ASN1_IA5_STRING: // 22 - case ASN1_GRAPHIC_STRING: // 25 - case ASN1_VISIBLE_STRING: // 26, also ASN1_ISO646_STRING - case ASN1_GENERAL_STRING: // 27 - case ASN1_UNIVERSAL_STRING: // 28 - { - CFStringRef string = - copyDERThingContentDescription(CFGetAllocator(properties), - currDecoded.tag, &currDecoded.content, false); - require_quiet(string, badSequence); - - appendPropertyP(properties, kSecPropertyTypeString, label, - string); - CFReleaseNull(string); - appendedSomething = true; - break; - } - default: - break; - } - } - require_quiet(drtn == DR_EndOfSequence, badSequence); - return appendedSomething; -badSequence: - return false; -} - -static void appendExtension(CFMutableArrayRef parent, - const SecCertificateExtension *extn) { - CFAllocatorRef allocator = CFGetAllocator(parent); - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - const DERItem - *extnID = &extn->extnID, - *extnValue = &extn->extnValue; - - appendBoolProperty(properties, CFSTR("Critical"), extn->critical); - -#if 1 - bool handled = true; - /* Extensions that we know how to handle ourselves... */ - if (extnID->length == oidSubjectKeyIdentifier.length && - !memcmp(extnID->data, oidSubjectKeyIdentifier.data, extnID->length - 1)) - { - switch (extnID->data[extnID->length - 1]) { - case 14: /* SubjectKeyIdentifier id-ce 14 */ - appendSubjectKeyIdentifier(properties, extnValue); - break; - case 15: /* KeyUsage id-ce 15 */ - appendKeyUsage(properties, extnValue); - break; - case 16: /* PrivateKeyUsagePeriod id-ce 16 */ - appendPrivateKeyUsagePeriod(properties, extnValue); - break; - case 17: /* SubjectAltName id-ce 17 */ - case 18: /* IssuerAltName id-ce 18 */ - appendGeneralNames(properties, extnValue); - break; - case 19: /* BasicConstraints id-ce 19 */ - appendBasicConstraints(properties, extnValue); - break; - case 30: /* NameConstraints id-ce 30 */ - handled = false; - break; - case 31: /* CRLDistributionPoints id-ce 31 */ - appendCrlDistributionPoints(properties, extnValue); - break; - case 32: /* CertificatePolicies id-ce 32 */ - appendCertificatePolicies(properties, extnValue); - break; - case 33: /* PolicyMappings id-ce 33 */ - handled = false; - break; - case 35: /* AuthorityKeyIdentifier id-ce 35 */ - appendAuthorityKeyIdentifier(properties, extnValue); - break; - case 36: /* PolicyConstraints id-ce 36 */ - appendPolicyConstraints(properties, extnValue); - break; - case 37: /* ExtKeyUsage id-ce 37 */ - appendExtendedKeyUsage(properties, extnValue); - break; - case 46: /* FreshestCRL id-ce 46 */ - handled = false; - break; - case 54: /* InhibitAnyPolicy id-ce 54 */ - handled = false; - break; - default: - handled = false; - break; - } - } else if (extnID->length == oidAuthorityInfoAccess.length && - !memcmp(extnID->data, oidAuthorityInfoAccess.data, extnID->length - 1)) - { - switch (extnID->data[extnID->length - 1]) { - case 1: /* AuthorityInfoAccess id-pe 1 */ - appendInfoAccess(properties, extnValue); - break; - case 3: /* QCStatements id-pe 3 */ - handled = false; - break; - case 11: /* SubjectInfoAccess id-pe 11 */ - appendInfoAccess(properties, extnValue); - break; - default: - handled = false; - break; - } - } else if (DEROidCompare(extnID, &oidNetscapeCertType)) { - /* 2.16.840.1.113730.1.1 netscape 1 1 */ - appendNetscapeCertType(properties, extnValue); - } else { - handled = false; - } - - if (!handled) { - /* Try to parse and display printable string(s). */ - if (appendPrintableDERSequenceP(properties, CFSTR("Data"), extnValue)) { - /* Nothing to do here appendPrintableDERSequenceP did the work. */ - } else { - /* Couldn't parse extension; dump the raw unparsed data as hex. */ - appendUnparsedProperty(properties, CFSTR("Data"), extnValue); - } - } -#else - /* Extensions that we know how to handle ourselves... */ - if (DEROidCompare(extnID, &oidSubjectKeyIdentifier)) { - appendSubjectKeyIdentifier(properties, extnValue); - } else if (DEROidCompare(extnID, &oidKeyUsage)) { - appendKeyUsage(properties, extnValue); - } else if (DEROidCompare(extnID, &oidPrivateKeyUsagePeriod)) { - appendPrivateKeyUsagePeriod(properties, extnValue); - } else if (DEROidCompare(extnID, &oidSubjectAltName)) { - appendGeneralNames(properties, extnValue); - } else if (DEROidCompare(extnID, &oidIssuerAltName)) { - appendGeneralNames(properties, extnValue); - } else if (DEROidCompare(extnID, &oidBasicConstraints)) { - appendBasicConstraints(properties, extnValue); - } else if (DEROidCompare(extnID, &oidCrlDistributionPoints)) { - appendCrlDistributionPoints(properties, extnValue); - } else if (DEROidCompare(extnID, &oidCertificatePolicies)) { - appendCertificatePolicies(properties, extnValue); - } else if (DEROidCompare(extnID, &oidAuthorityKeyIdentifier)) { - appendAuthorityKeyIdentifier(properties, extnValue); - } else if (DEROidCompare(extnID, &oidPolicyConstraints)) { - appendPolicyConstraints(properties, extnValue); - } else if (DEROidCompare(extnID, &oidExtendedKeyUsage)) { - appendExtendedKeyUsage(properties, extnValue); - } else if (DEROidCompare(extnID, &oidAuthorityInfoAccess)) { - appendInfoAccess(properties, extnValue); - } else if (DEROidCompare(extnID, &oidSubjectInfoAccess)) { - appendInfoAccess(properties, extnValue); - } else if (DEROidCompare(extnID, &oidNetscapeCertType)) { - appendNetscapeCertType(properties, extnValue); -#if 0 - } else if (DEROidCompare(extnID, &oidEntrustVersInfo)) { - appendEntrustVersInfo(properties, extnValue); -#endif - } else - /* Try to parse and display printable string(s). */ - if (appendPrintableDERSequenceP(properties, CFSTR("Data"), extnValue)) { - /* Nothing to do here appendPrintableDERSequenceP did the work. */ - } else { - /* Couldn't parse extension; dump the raw unparsed data as hex. */ - appendUnparsedProperty(properties, CFSTR("Data"), extnValue); - } -#endif - CFStringRef oid_string = copyLocalizedOidDescription(allocator, extnID); - appendPropertyP(parent, kSecPropertyTypeSection, oid_string, properties); - CFRelease(oid_string); - CFRelease(properties); -} - -/* Different types of summary types from least desired to most desired. */ -enum SummaryType { - kSummaryTypeNone, - kSummaryTypePrintable, - kSummaryTypeOrganizationName, - kSummaryTypeOrganizationalUnitName, - kSummaryTypeCommonName, -}; - -struct Summary { - enum SummaryType type; - CFStringRef summary; - CFStringRef description; -}; - -static OSStatus obtainSummaryFromX501Name(void *context, - const DERItem *type, const DERItem *value, CFIndex rdnIX) { - struct Summary *summary = (struct Summary *)context; - enum SummaryType stype = kSummaryTypeNone; - CFStringRef string = NULL; - if (DEROidCompare(type, &oidCommonName)) { - /* We skip Common Names that have generic values. */ - const char tfm[] = "Thawte Freemail Member"; - if ((value->length == sizeof(tfm) + 1) && - !memcmp(value->data + 2, tfm, sizeof(tfm) - 1)) { - return errSecSuccess; - } - stype = kSummaryTypeCommonName; - } else if (DEROidCompare(type, &oidOrganizationalUnitName)) { - stype = kSummaryTypeOrganizationalUnitName; - } else if (DEROidCompare(type, &oidOrganizationName)) { - stype = kSummaryTypeOrganizationName; - } else if (DEROidCompare(type, &oidDescription)) { - if (!summary->description) { - summary->description = string = copyDERThingDescription(kCFAllocatorDefault, value, true); - CFRetain(string); - } - stype = kSummaryTypePrintable; - } else { - stype = kSummaryTypePrintable; - } - - /* Use the first field we encounter of the highest priority type. */ - if (summary->type < stype) { - if (!string) { - string = copyDERThingDescription(kCFAllocatorDefault, value, true); - } - - if (string) { - CFReleaseSafe(summary->summary); - summary->summary = string; - summary->type = stype; - } - } else { - CFReleaseSafe(string); - } - - return errSecSuccess; -} - -CFStringRef SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate) { - struct Summary summary = {}; - parseX501NameContent(&certificate->_subject, &summary, obtainSummaryFromX501Name); - /* If we found a description and a common name we change the summary to - CommonName (Description). */ - if (summary.description) { - if (summary.type == kSummaryTypeCommonName) { - CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("%@ (%@)"), summary.summary, summary.description); - CFRelease(summary.summary); - summary.summary = newSummary; - } - CFRelease(summary.description); - } - - if (!summary.summary) { - /* If we didn't find a suitable printable string in the subject at all, we try - the first email address in the certificate instead. */ - CFArrayRef names = SecCertificateCopyRFC822NamesP(certificate); - if (!names) { - /* If we didn't find any email addresses in the certificate, we try finding - a DNS name instead. */ - names = SecCertificateCopyDNSNamesP(certificate); - } - if (names) { - summary.summary = CFArrayGetValueAtIndex(names, 0); - CFRetain(summary.summary); - CFRelease(names); - } - } - - return summary.summary; -} - -CFStringRef SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate) { - struct Summary summary = {}; - parseX501NameContent(&certificate->_issuer, &summary, obtainSummaryFromX501Name); - /* If we found a description and a common name we change the summary to - CommonName (Description). */ - if (summary.description) { - if (summary.type == kSummaryTypeCommonName) { - CFStringRef newSummary = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("%@ (%@)"), summary.summary, summary.description); - CFRelease(summary.summary); - summary.summary = newSummary; - } - CFRelease(summary.description); - } - - return summary.summary; -} - -/* Return the earliest date on which all certificates in this chain are still - valid. */ -static CFAbsoluteTime SecCertificateGetChainsLastValidity( - SecCertificateRefP certificate) { - CFAbsoluteTime earliest = certificate->_notAfter; -#if 0 - while (certificate->_parent) { - certificate = certificate->_parent; - if (earliest > certificate->_notAfter) - earliest = certificate->_notAfter; - } -#endif - - return earliest; -} - -/* Return the latest date on which all certificates in this chain will be - valid. */ -static CFAbsoluteTime SecCertificateGetChainsFirstValidity( - SecCertificateRefP certificate) { - CFAbsoluteTime latest = certificate->_notBefore; -#if 0 - while (certificate->_parent) { - certificate = certificate->_parent; - if (latest < certificate->_notBefore) - latest = certificate->_notBefore; - } -#endif - - return latest; -} - -bool SecCertificateIsValidP(SecCertificateRefP certificate, - CFAbsoluteTime verifyTime) { - check(certificate); - return certificate->_notBefore <= verifyTime && - verifyTime <= certificate->_notAfter; -} - -CFIndex SecCertificateVersionP(SecCertificateRefP certificate) { - return certificate->_version + 1; -} - -CFAbsoluteTime SecCertificateNotValidBeforeP(SecCertificateRefP certificate) { - return certificate->_notBefore; -} - -CFAbsoluteTime SecCertificateNotValidAfterP(SecCertificateRefP certificate) { - return certificate->_notAfter; -} - -CFMutableArrayRef SecCertificateCopySummaryPropertiesP( - SecCertificateRefP certificate, CFAbsoluteTime verifyTime) { - CFAllocatorRef allocator = CFGetAllocator(certificate); - CFMutableArrayRef summary = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - - /* First we put the subject summary name. */ - CFStringRef ssummary = SecCertificateCopySubjectSummaryP(certificate); - if (ssummary) { - appendPropertyP(summary, kSecPropertyTypeTitle, - NULL, ssummary); - CFRelease(ssummary); - } -#if 0 - CFStringRef isummary = CFSTR("Issuer Summary"); - appendPropertyP(summary, kSecPropertyTypeString, - CFSTR("Issued By"), isummary); - CFRelease(isummary); -#endif - - /* Let see if this certificate is currently valid. */ - CFStringRef label; - CFAbsoluteTime when; - CFStringRef message; - CFStringRef ptype; - if (verifyTime > certificate->_notAfter) { - label = CFSTR("Expired"); - when = certificate->_notAfter; - ptype = kSecPropertyTypeError; - message = CFSTR("This certificate has expired"); - } else if (certificate->_notBefore > verifyTime) { - label = CFSTR("Valid from"); - when = certificate->_notBefore; - ptype = kSecPropertyTypeError; - message = CFSTR("This certificate is not yet valid"); - } else { - CFAbsoluteTime last = SecCertificateGetChainsLastValidity(certificate); - CFAbsoluteTime first = SecCertificateGetChainsFirstValidity(certificate); - if (verifyTime > last) { - label = CFSTR("Expired"); - when = last; - ptype = kSecPropertyTypeError; - message = CFSTR("This certificate has an issuer that has expired"); - } else if (verifyTime < first) { - label = CFSTR("Valid from"); - when = first; - ptype = kSecPropertyTypeError; - message = CFSTR("This certificate has an issuer that is not yet valid"); - } else { - label = CFSTR("Expires"); - when = certificate->_notAfter; - ptype = kSecPropertyTypeSuccess; - message = CFSTR("This certificate is valid"); - } - } - - appendDateProperty(summary, label, when); - appendPropertyP(summary, ptype, NULL, message); - - return summary; -} - -CFArrayRef SecCertificateCopyPropertiesP(SecCertificateRefP certificate) { - if (!certificate->_properties) { - CFAllocatorRef allocator = CFGetAllocator(certificate); - CFMutableArrayRef properties = CFArrayCreateMutable(allocator, 0, - &kCFTypeArrayCallBacks); - require_quiet(properties, out); - - /* First we put the Subject Name in the property list. */ - CFArrayRef subject_plist = createPropertiesForX501NameContent(allocator, - &certificate->_subject); - if (subject_plist) { - appendPropertyP(properties, kSecPropertyTypeSection, - CFSTR("Subject Name"), subject_plist); - } - CFReleaseNull(subject_plist); - - /* Next we put the Issuer Name in the property list. */ - CFArrayRef issuer_plist = createPropertiesForX501NameContent(allocator, - &certificate->_issuer); - if (issuer_plist) { - appendPropertyP(properties, kSecPropertyTypeSection, - CFSTR("Issuer Name"), issuer_plist); - } - CFReleaseNull(issuer_plist); - - /* Version */ - CFStringRef versionString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), - certificate->_version + 1); - if (versionString) { - appendPropertyP(properties, kSecPropertyTypeString, - CFSTR("Version"), versionString); - } - CFReleaseNull(versionString); - - /* Serial Number */ - if (certificate->_serialNum.length) { - appendIntegerProperty(properties, CFSTR("Serial Number"), - &certificate->_serialNum); - } - - /* Signature algorithm. */ - appendAlgorithmProperty(properties, CFSTR("Signature Algorithm"), - &certificate->_tbsSigAlg); - - - /* Validity dates. */ - appendDateProperty(properties, CFSTR("Not Valid Before"), - certificate->_notBefore); - appendDateProperty(properties, CFSTR("Not Valid After"), - certificate->_notAfter); - - if (certificate->_subjectUniqueID.length) { - appendDataProperty(properties, CFSTR("Subject Unique ID"), - &certificate->_subjectUniqueID); - } - if (certificate->_issuerUniqueID.length) { - appendDataProperty(properties, CFSTR("Issuer Unique ID"), - &certificate->_issuerUniqueID); - } - - /* Public key algorithm. */ - appendAlgorithmProperty(properties, CFSTR("Public Key Algorithm"), - &certificate->_algId); - - /* Consider breaking down an RSA public key into modulus and - exponent? */ - appendDataProperty(properties, CFSTR("Public Key Data"), - &certificate->_pubKeyDER); - /* @@@ Key Size. */ - /* @@@ Key Usage. */ - - appendDataProperty(properties, CFSTR("Signature"), - &certificate->_signature); - - CFIndex ix; - for (ix = 0; ix < certificate->_extensionCount; ++ix) { - appendExtension(properties, &certificate->_extensions[ix]); - } - - /* @@@ Key Fingerprints. */ - - certificate->_properties = properties; - } - -out: - CFRetainSafe(certificate->_properties); - return certificate->_properties; -} - -CFDataRef SecCertificateCopySerialNumberP( - SecCertificateRefP certificate) { - if (certificate->_serialNumber) { - CFRetain(certificate->_serialNumber); - } - return certificate->_serialNumber; -} - -/* - * Accessor for normalized issuer content - */ -CFDataRef SecCertificateGetNormalizedIssuerContentP( - SecCertificateRefP certificate) { - return certificate->_normalizedIssuer; -} - -/* - * Accessor for normalized subject content - */ -CFDataRef SecCertificateGetNormalizedSubjectContentP( - SecCertificateRefP certificate) { - return certificate->_normalizedSubject; -} - -/* - * Returns DER-encoded normalized issuer sequence - * for use with SecItemCopyMatching; caller must release - */ -CFDataRef SecCertificateCopyNormalizedIssuerSequenceP( - SecCertificateRefP certificate) { - if (!certificate || !certificate->_normalizedIssuer) { - return NULL; - } - DERItem tmpdi; - tmpdi.data = (DERByte *)CFDataGetBytePtr(certificate->_normalizedIssuer); - tmpdi.length = CFDataGetLength(certificate->_normalizedIssuer); - - return SecDERItemCopySequenceP(&tmpdi); -} - -/* - * Returns DER-encoded normalized subject sequence - * for use with SecItemCopyMatching; caller must release - */ -CFDataRef SecCertificateCopyNormalizedSubjectSequenceP( - SecCertificateRefP certificate) { - if (!certificate || !certificate->_normalizedSubject) { - return NULL; - } - DERItem tmpdi; - tmpdi.data = (DERByte *)CFDataGetBytePtr(certificate->_normalizedSubject); - tmpdi.length = CFDataGetLength(certificate->_normalizedSubject); - - return SecDERItemCopySequenceP(&tmpdi); -} - -/* Verify that certificate was signed by issuerKey. */ -OSStatus SecCertificateIsSignedByP(SecCertificateRefP certificate, - SecKeyRefP issuerKey) { - /* Setup algId in SecAsn1AlgId format. */ - SecAsn1AlgId algId; - algId.algorithm.Length = certificate->_tbsSigAlg.oid.length; - algId.algorithm.Data = certificate->_tbsSigAlg.oid.data; - algId.parameters.Length = certificate->_tbsSigAlg.params.length; - algId.parameters.Data = certificate->_tbsSigAlg.params.data; - -#if 0 - OSStatus status = SecKeyDigestAndVerify(issuerKey, &algId, - certificate->_tbs.data, certificate->_tbs.length, - certificate->_signature.data, certificate->_signature.length); - if (status) { - secinfo("verify", "signature verify failed: %d", status); - return errSecNotSigner; - } -#endif - - return errSecSuccess; -} - -#if 0 -static OSStatus SecCertificateIsIssuedBy(SecCertificateRefP certificate, - SecCertificateRefP issuer, bool signatureCheckOnly) { - if (!signatureCheckOnly) { - /* It turns out we don't actually need to use normalized subject and - issuer according to rfc2459. */ - - /* If present we should check issuerID against the issuer subjectID. */ - - /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier - then we should look for a SubjectKeyIdentifier in the issuer - certificate. - If we have a authorityCertSerialNumber we can use that for chaining. - If we have a authorityCertIssuer we can use that? (or not) */ - - /* Verify that this cert was issued by issuer. Do so by chaining - either issuerID to subjectID or normalized issuer to normalized - subject. */ - CFDataRef normalizedIssuer = - SecCertificateGetNormalizedIssuerContentP(certificate); - CFDataRef normalizedIssuerSubject = - SecCertificateGetNormalizedSubjectContentP(issuer); - if (normalizedIssuer && normalizedIssuerSubject && - !CFEqual(normalizedIssuer, normalizedIssuerSubject)) - return errSecIssuerMismatch; - } - - /* Next verify that this cert was signed by issuer. */ - SecKeyRef issuerKey = SecCertificateGetPublicKey(issuer); - - /* Get the encodedDigestInfo from the digest of the subject's TBSCert */ - /* FIXME: We sould cache this (or at least the digest) until we find - a suitable issuer. */ - uint8_t signedData[DER_SHA1_DIGEST_INFO_LEN]; - CFIndex signedDataLength; - CertVerifyReturn crtn; - if (DEROidCompare(&certificate->_tbsSigAlg.oid, &oidSha1Rsa)) { - signedDataLength = DER_SHA1_DIGEST_INFO_LEN; - crtn = sha1DigestInfo(&certificate->_tbs, signedData); - } else if(DEROidCompare(&certificate->_tbsSigAlg.oid, &oidMd5Rsa)) { - signedDataLength = DER_MD_DIGEST_INFO_LEN; - crtn = mdDigestInfo(WD_MD5, &certificate->_tbs, signedData); - } else if(DEROidCompare(&certificate->_tbsSigAlg.oid, &oidMd2Rsa)) { - signedDataLength = DER_MD_DIGEST_INFO_LEN; - crtn = mdDigestInfo(WD_MD2, &certificate->_tbs, signedData); - } else { - secinfo("verify", "unsupported algorithm"); - return errSecUnsupportedAlgorithm; - } - if (crtn) { - secinfo("verify", "*DigestInfo returned: %d", crtn); - /* FIXME: Do proper error code translation. */ - return errSecUnsupportedAlgorithm; - } - - OSStatus status = SecKeyRawVerify(issuerKey, kSecPaddingPKCS1, - signedData, signedDataLength, - certificate->_signature.data, certificate->_signature.length); - if (status) { - secinfo("verify", "signature verify failed: %d", status); - return errSecNotSigner; - } - - return errSecSuccess; -} - -static OSStatus _SecCertificateSetParent(SecCertificateRefP certificate, - SecCertificateRefP issuer, bool signatureCheckOnly) { - check(issuer); - if (certificate->_parent) { - /* Setting a certificates issuer twice is only allowed if the new - issuer is equal to the current one. */ - return issuer && CFEqual(certificate->_parent, issuer); - } - -#if 0 - OSStatus status = SecCertificateIsIssuedBy(certificate, issuer, - signatureCheckOnly); -#else - OSStatus status = errSecSuccess; -#endif - if (!status) { - if (CFEqual(certificate, issuer)) { - /* We don't retain ourselves cause that would be bad mojo, - however we do record that we are properly self signed. */ - certificate->_isSelfSigned = kSecSelfSignedTrue; - secinfo("cert", "set self as parent"); - return errSecSuccess; - } - - CFRetain(issuer); - certificate->_parent = issuer; - certificate->_isSelfSigned = kSecSelfSignedFalse; - } - - return status; -} - -static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate) { - if (certificate->_isSelfSigned == kSecSelfSignedUnknown) { - certificate->_isSelfSigned = - (SecCertificateIsIssuedBy(certificate, certificate, false) ? - kSecSelfSignedTrue : kSecSelfSignedFalse); - } - - return certificate->_isSelfSigned == kSecSelfSignedTrue; -} - -/* Return true iff we were able to set our own parent from one of the - certificates in other_certificates, return false otherwise. If - signatureCheckOnly is true, we can skip the subject == issuer or - authorityKeyIdentifier tests. */ -static bool SecCertificateSetParentFrom(SecCertificateRefP certificate, - CFArrayRef other_certificates, bool signatureCheckOnly) { - CFIndex count = CFArrayGetCount(other_certificates); - CFIndex ix; - for (ix = 0; ix < count; ++ix) { - SecCertificateRefP candidate = (SecCertificateRefP) - CFArrayGetValueAtIndex(other_certificates, ix); - if (_SecCertificateSetParent(certificate, candidate, - signatureCheckOnly)) - return true; - } - return false; -} - -/* Lookup the parent of certificate in the keychain and set it. */ -static bool SecCertificateFindParent(SecCertificateRefP certificate) { - /* FIXME: Search for things other than just subject of our issuer if we - have a subjectID or authorityKeyIdentifier. */ - CFDataRef normalizedIssuer = - SecCertificateGetNormalizedIssuerContentP(certificate); - const void *keys[] = { - kSecClass, - kSecReturnRef, - kSecMatchLimit, - kSecAttrSubject - }, - *values[] = { - kSecClassCertificate, - kCFBooleanTrue, - kSecMatchLimitAll, - normalizedIssuer - }; - CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 4, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFTypeRef results; - OSStatus status = SecItemCopyMatching(query, &results); - CFRelease(query); - if (status) { - secinfo("cert", "SecCertificateFindParent: SecItemCopyMatching: %d", - status); - return false; - } - CFArrayRef certs = (CFArrayRef)results; - /* Since we already know the certificates we are providing as candidates - have been checked for subject matching, we can ask - SecCertificateSetParentFrom to skip everything except the signature - checks. */ - bool result = SecCertificateSetParentFrom(certificate, certs, true); - CFRelease(certs); - return result; -} - -OSStatus SecCertificateCompleteChainP(SecCertificateRefP certificate, - CFArrayRef other_certificates) { - for (;;) { - if (certificate->_parent == NULL) { - if (SecCertificateIsSelfSignedP(certificate)) - return errSecSuccess; - if (!other_certificates || - !SecCertificateSetParentFrom(certificate, other_certificates,\ - false)) { - if (!SecCertificateFindParent(certificate)) - return errSecIssuerNotFound; - } - } - certificate = certificate->_parent; - } -} -#endif - -static OSStatus appendIPAddressesFromGeneralNames(void *context, - SecCEGeneralNameType gnType, const DERItem *generalName) { - CFMutableArrayRef ipAddresses = (CFMutableArrayRef)context; - if (gnType == GNT_IPAddress) { - CFStringRef string = copyIPAddressContentDescription( - kCFAllocatorDefault, generalName); - if (string) { - CFArrayAppendValue(ipAddresses, string); - CFRelease(string); - } else { - return errSecInvalidCertificate; - } - } - return errSecSuccess; -} - -CFArrayRef SecCertificateCopyIPAddressesP(SecCertificateRefP certificate) { - /* These can only exist in the subject alt name. */ - if (!certificate->_subjectAltName) - return NULL; - - CFMutableArrayRef ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault, - 0, &kCFTypeArrayCallBacks); - OSStatus status = parseGeneralNames(&certificate->_subjectAltName->extnValue, - ipAddresses, appendIPAddressesFromGeneralNames); - if (status || CFArrayGetCount(ipAddresses) == 0) { - CFRelease(ipAddresses); - ipAddresses = NULL; - } - return ipAddresses; -} - -static OSStatus appendDNSNamesFromGeneralNames(void *context, SecCEGeneralNameType gnType, - const DERItem *generalName) { - CFMutableArrayRef dnsNames = (CFMutableArrayRef)context; - if (gnType == GNT_DNSName) { - CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, - generalName->data, generalName->length, - kCFStringEncodingUTF8, FALSE); - if (string) { - CFArrayAppendValue(dnsNames, string); - CFRelease(string); - } else { - return errSecInvalidCertificate; - } - } - return errSecSuccess; -} - -/* Return true if the passed in string matches the - Preferred name syntax from sections 2.3.1. in RFC 1035. - With the added check that we disallow empty dns names. - Also in order to support wildcard DNSNames we allow for the '*' - character anywhere in a dns component where we currently allow - a letter. - - ::= | " " - - ::=