CFErrorRef error = NULL;
CFErrorRef departError = NULL;
- SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircleNonCached(&error);
+ SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&error);
enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&departError);
// Error due to XPC failure does not provide information about the circle.
}
PSKeychainSyncIsUsingICDP();
-
- // Refresh because sometimes we're fixed elsewhere before we get here.
- CFReleaseNull(error);
- circleStatus = SOSCCThisDeviceIsInCircleNonCached(&error);
-
+
if(_isAccountICDP){
if((circleStatus == kSOSCCError || circleStatus == kSOSCCCircleAbsent || circleStatus == kSOSCCNotInCircle) && _hasPostedFollowupAndStillInError == false) {
- if(circleStatus == kSOSCCError) {
- secnotice("cjr", "error from SOSCCThisDeviceIsInCircle: %@", error);
- }
+ secnotice("cjr", "error from SOSCCThisDeviceIsInCircle: %@", error);
secnotice("cjr", "iCDP: We need to get back into the circle");
doOnceInMain(^{
- NSError *localError = nil;
- CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
- CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
- [cdpd postFollowUpWithContext:context error:&localError ];
- if(localError){
- secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
+ if(_isAccountICDP){
+ NSError *localError = nil;
+ CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
+ CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+ [cdpd postFollowUpWithContext:context error:&localError ];
+ if(localError){
+ secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
+ }
+ else{
+ secnotice("cjr", "CoreCDP handling follow up");
+ _hasPostedFollowupAndStillInError = true;
+ }
}
else{
- secnotice("cjr", "CoreCDP handling follow up");
- _hasPostedFollowupAndStillInError = true;
+ postKickedOutAlert(kSOSPasswordChanged);
+ state.lastCircleStatus = kSOSCCError;
+ [state writeToStorage];
}
});
state.lastCircleStatus = circleStatus;
_executeProcessEventsOnce = true;
return false;
}
- } else if(circleStatus == kSOSCCError && state.lastCircleStatus != kSOSCCError && (departureReason == kSOSNeverLeftCircle)) {
+ }
+ else if(circleStatus == kSOSCCError && state.lastCircleStatus != kSOSCCError && (departureReason == kSOSNeverLeftCircle)
+ && !_isAccountICDP) {
secnotice("cjr", "SA: error from SOSCCThisDeviceIsInCircle: %@", error);
CFIndex errorCode = CFErrorGetCode(error);
if(errorCode == kSOSErrorPublicKeyAbsent){
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
#import <MobileKeyBag/MobileKeyBag.h>
+#include <System/sys/content_protection.h>
#endif
#if TARGET_OS_OSX
return NO;
}
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+static bool ChangeFileProtectionToClassD(NSURL *fileURL, NSError **error) {
+ BOOL result = YES;
+ int file_fd = open([fileURL fileSystemRepresentation], O_RDONLY);
+ if (file_fd) {
+ int retval = fcntl(file_fd, F_SETPROTECTIONCLASS, PROTECTION_CLASS_D);
+ if (retval < 0) {
+ MakeOTATrustError(error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
+ @"set proteciton class error for asset %d: %s", errno, strerror(errno));
+ result = NO;
+ }
+ close(file_fd);
+ } else {
+ MakeOTATrustError(error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
+ @"open error for asset %d: %s", errno, strerror(errno));
+ result = NO;
+ }
+ return result;
+}
+#endif
+
static BOOL CopyFileToDisk(NSString *filename, NSURL *localURL, NSError **error) {
if (SecOTAPKIIsSystemTrustd()) {
NSURL *toFileURL = GetAssetFileURL(filename);
@"copyfile error for asset %d: %s", errno, strerror(errno));
return NO;
} else {
+ /* make sure we can read this file before first unlock */
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+ return ChangeFileProtectionToClassD(toFileURL, error);
+#else
return YES;
+#endif
}
}
return NO;
#define _SECURITY_SECTRUSTLOGGINGSERVER_H_
+void DisableLocalization(void);
+
#endif /* _SECURITY_SECTRUSTLOGGINGSERVER_H_ */
int main(int argc, char *argv[])
{
+ DisableLocalization();
+
char *wait4debugger = getenv("WAIT4DEBUGGER");
if (wait4debugger && !strcasecmp("YES", wait4debugger)) {
seccritical("SIGSTOPing self, awaiting debugger");
@interface CKKSAPSReceiver()
-// If we receive >0 notifications for a CKRecordZoneID that hasn't been registered yet, give them a fake update when they register
-@property NSMutableSet<CKRecordZoneID*>* undeliveredUpdates;
+// If we receive notifications for a CKRecordZoneID that hasn't been registered yet, send them a their updates when they register
+@property NSMutableDictionary<CKRecordZoneID*, NSMutableSet<CKRecordZoneNotification*>*>* undeliveredUpdates;
@end
@implementation CKKSAPSReceiver
_apsConnectionClass = apsConnectionClass;
_apsConnection = NULL;
- _undeliveredUpdates = [[NSMutableSet alloc] init];
+ _undeliveredUpdates = [NSMutableDictionary dictionary];
// APS might be slow. This doesn't need to happen immediately, so let it happen later.
__weak __typeof(self) weakSelf = self;
}
[self.zoneMap setObject:receiver forKey: zoneID];
- if([strongSelf.undeliveredUpdates containsObject:zoneID]) {
- [strongSelf.undeliveredUpdates removeObject:zoneID];
- // Now, send the receiver its fake notification!
- secerror("ckks: sending fake push to newly-registered zone(%@): %@", zoneID, receiver);
- [receiver notifyZoneChange:nil];
+ NSMutableSet<CKRecordZoneNotification*>* currentPendingMessages = self.undeliveredUpdates[zoneID];
+ [self.undeliveredUpdates removeObjectForKey:zoneID];
+
+ for(CKRecordZoneNotification* message in currentPendingMessages.allObjects) {
+ // Now, send the receiver its notification!
+ secerror("ckks: sending stored push(%@) to newly-registered zone(%@): %@", message, zoneID, receiver);
+ [receiver notifyZoneChange:message];
}
[finished fulfill];
} else {
secerror("ckks: received push for unregistered zone: %@", rznotification);
if(rznotification.recordZoneID) {
- // TODO: save the rznofication itself
- [self.undeliveredUpdates addObject: rznotification.recordZoneID];
+ NSMutableSet<CKRecordZoneNotification*>* currentPendingMessages = self.undeliveredUpdates[rznotification.recordZoneID];
+ if(currentPendingMessages) {
+ [currentPendingMessages addObject:rznotification];
+ } else {
+ self.undeliveredUpdates[rznotification.recordZoneID] = [NSMutableSet setWithObject:rznotification];
+ }
}
}
} else {
[super tearDown];
}
++ (APSIncomingMessage*)messageWithTracingEnabledForZoneID:(CKRecordZoneID*)zoneID {
+ APSIncomingMessage* apsMessage = [CKKSAPSReceiverTests messageForZoneID:zoneID];
+ NSUUID* nsuuid = [NSUUID UUID];
+ uuid_t uuid = {0};
+ [nsuuid getUUIDBytes:(unsigned char*)&uuid];
+ NSData* uuidData = [NSData dataWithBytes:&uuid length:sizeof(uuid)];
+
+ apsMessage.tracingUUID = uuidData;
+ apsMessage.tracingEnabled = YES;
+
+ return apsMessage;
+}
+
- (void)testSendPushMetricUponRequest {
for(CKRecordZoneID* zoneID in self.ckksZones) {
[self putFakeKeyHierarchyInCloudKit:zoneID];
[manateeProcessTimeoutOp addSuccessDependency:manateeProcessOp];
[self.operationQueue addOperation:manateeProcessTimeoutOp];
- APSIncomingMessage* apsMessage = [CKKSAPSReceiverTests messageForZoneID:self.keychainZoneID];
- NSUUID* nsuuid = [NSUUID UUID];
- uuid_t uuid = {0};
- [nsuuid getUUIDBytes:(unsigned char*)&uuid];
- NSData* uuidData = [NSData dataWithBytes:&uuid length:sizeof(uuid)];
-
- apsMessage.tracingUUID = uuidData;
- apsMessage.tracingEnabled = YES;
+ APSIncomingMessage* apsMessage = [CKKSAPSHandlingTests messageWithTracingEnabledForZoneID:self.keychainZoneID];
// Inject a message at the APS layer
// Because we can only make APS receivers once iCloud tells us the push environment after sign-in, we can't use our normal injection strategy, and fell back on global state.
[keychainProcessTimeoutOp addSuccessDependency:keychainProcessOp];
[self.operationQueue addOperation:keychainProcessTimeoutOp];
- APSIncomingMessage* apsMessage = [CKKSAPSReceiverTests messageForZoneID:self.keychainZoneID];
- NSUUID* nsuuid = [NSUUID UUID];
- uuid_t uuid = {0};
- [nsuuid getUUIDBytes:(unsigned char*)&uuid];
- NSData* uuidData = [NSData dataWithBytes:&uuid length:sizeof(uuid)];
-
- apsMessage.tracingUUID = uuidData;
+ // Create a push that matchs all push tracing patterns except for the enabled flag
+ APSIncomingMessage* apsMessage = [CKKSAPSHandlingTests messageWithTracingEnabledForZoneID:self.keychainZoneID];
apsMessage.tracingEnabled = NO;
// Inject a message at the APS layer
[self findGenericPassword:@"keychain-view" expecting:errSecSuccess];
}
+- (void)testSendPushMetricEvenIfPushArrivesEarly {
+ CKRecordZoneID* pushTestZone = [[CKRecordZoneID alloc] initWithZoneName:@"PushTestZone" ownerName:CKCurrentUserDefaultName];
+ [self.ckksZones addObject:pushTestZone];
+ self.zones[pushTestZone] = [[FakeCKZone alloc] initZone: pushTestZone];
+
+ for(CKRecordZoneID* zoneID in self.ckksZones) {
+ [self putFakeKeyHierarchyInCloudKit:zoneID];
+ [self saveTLKMaterialToKeychain:zoneID];
+ [self expectCKKSTLKSelfShareUpload:zoneID];
+ }
+
+ // The push wakes securityd, so it happens before pushTestZone is created locally
+ // Send 2, just to test our infrastructure
+ APSIncomingMessage* apsMessage = [CKKSAPSHandlingTests messageWithTracingEnabledForZoneID:pushTestZone];
+ APSIncomingMessage* apsMessage2 = [CKKSAPSHandlingTests messageWithTracingEnabledForZoneID:pushTestZone];
+
+ // Inject a message at the APS layer
+ // Because we can only make APS receivers once iCloud tells us the push environment after sign-in, we can't use our normal injection strategy, and fell back on global state.
+ CKKSAPSReceiver* apsReceiver = [CKKSAPSReceiver receiverForEnvironment:self.apsEnvironment
+ namedDelegatePort:SecCKKSAPSNamedPort
+ apsConnectionClass:[FakeAPSConnection class]];
+ XCTAssertNotNil(apsReceiver, "Should have gotten an APS receiver");
+
+ [apsReceiver connection:nil didReceiveIncomingMessage:apsMessage];
+ [apsReceiver connection:nil didReceiveIncomingMessage:apsMessage2];
+
+ // Expect four metric pushes, two per push: one from receiving the push and one from after we finish the fetch
+ // AFAICT there's no way to introspect a metric object to ensure we did it right
+ OCMExpect([self.mockContainerExpectations submitEventMetric:[OCMArg any]]);
+ OCMExpect([self.mockContainerExpectations submitEventMetric:[OCMArg any]]);
+ OCMExpect([self.mockContainerExpectations submitEventMetric:[OCMArg any]]);
+ OCMExpect([self.mockContainerExpectations submitEventMetric:[OCMArg any]]);
+
+ // Launch!
+ [self.injectedManager findOrCreateView:pushTestZone.zoneName];
+
+ [self startCKKSSubsystem];
+
+ for(CKKSKeychainView* view in self.ckksViews) {
+ XCTAssertEqual(0, [view.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "Key state should enter 'ready' for view %@", view);
+ }
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
@end
#endif
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
-- (void)testReceiveNullNotificationIfRegisteredAfterDelivery {
+- (void)testReceiveNotificationIfRegisteredAfterDelivery {
CKKSAPSReceiver* apsr = [[CKKSAPSReceiver alloc] initWithEnvironmentName:@"testenvironment"
namedDelegatePort:SecCKKSAPSNamedPort
apsConnectionClass:[FakeAPSConnection class]];
CKKSAPSNotificationReceiver* anr = [[CKKSAPSNotificationReceiver alloc] initWithExpectation:[self expectationWithDescription:@"receive notification"]
block:
^(CKRecordZoneNotification* notification) {
- XCTAssertNil(notification, "Should not have received a notification, since we weren't alive to receive it");
+ XCTAssertNotNil(notification, "Should have received a (stored) notification");
}];
CKKSCondition* registered = [apsr registerReceiver:anr forZoneID:self.testZoneID];