]> git.saurik.com Git - apple/security.git/commitdiff
Security-59306.120.7.tar.gz macos-10155 v59306.120.7
authorApple <opensource@apple.com>
Wed, 3 Jun 2020 04:21:43 +0000 (04:21 +0000)
committerApple <opensource@apple.com>
Wed, 3 Jun 2020 04:21:43 +0000 (04:21 +0000)
22 files changed:
OSX/authd/authtoken.c
OSX/authd/engine.c
OSX/authd/process.c
OSX/sec/Security/SecItem.c
OSX/sec/ipc/server.c
OSX/sec/ipc/server_security_helpers.h
OSX/sec/ipc/server_security_helpers.m
keychain/SecureObjectSync/SOSAccount.h
keychain/SecureObjectSync/SOSAccount.m
keychain/SecureObjectSync/SOSAccountCircles.m
keychain/SecureObjectSync/SOSAccountCredentials.m
keychain/SecureObjectSync/SOSAccountPriv.h
keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.m
keychain/SecureObjectSync/SOSCircle.c
keychain/TrustedPeersHelper/Container.swift
keychain/ot/OTCuttlefishContext.m
keychain/ot/tests/octagon/OctagonTests+Reset.swift
keychain/securityd/Regressions/SOSAccountTesting.h
keychain/securityd/Regressions/secd-100-initialsync.m
keychain/securityd/Regressions/secd-55-account-circle.m
keychain/securityd/Regressions/secd-58-password-change.m
keychain/securityd/SOSCloudCircleServer.m

index 6b0568aab1ff88e0d2f618e5f53f6f69cb286be2..611d44150277d906c8d25ac268e14199c5405428 100644 (file)
@@ -395,7 +395,8 @@ auth_token_has_entitlement(auth_token_t auth, const char * entitlement)
             entitled = process_has_entitlement(auth->creator, entitlement);
         }
     });
-    
+    os_log_debug(AUTHD_LOG, "authtoken: PID %d is%{public}s entitled for %{public}s", auth->auditInfo.pid, entitled ? "":" not", entitlement);
+
     return entitled;
 }
 
@@ -409,7 +410,8 @@ auth_token_has_entitlement_for_right(auth_token_t auth, const char * right)
             entitled = process_has_entitlement_for_right(auth->creator, right);
         }
     });
-    
+    os_log_debug(AUTHD_LOG, "authtoken: PID %d is%{public}s entitled for right %{public}s", auth->auditInfo.pid, entitled ? "":" not", right);
+
     return entitled;
 }
 
index 1e4584db4c6e8ff8906d9b83abc2f61b79ce4a55..7df8370caa2f7b3eaca73797b0b78d9fa6be1d2c 100644 (file)
@@ -612,6 +612,7 @@ _evaluate_mechanisms(engine_t engine, CFArrayRef mechanisms)
     }
 
 done:
+    auth_items_set_flags(context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME, kAuthorizationContextFlagExtractable);
     if ((result == kAuthorizationResultUserCanceled) || (result == kAuthorizationResultAllow)) {
         // only make non-sticky context values available externally
         auth_items_set_flags(context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagVolatile);
index 4a2457577b731e62d16b15c38f8f950125bb6687..da50d54203809610a6829e7aa8a6ea8c1e811083 100644 (file)
@@ -159,6 +159,9 @@ process_create(const audit_info_s * auditInfo, session_t session)
         goto done;
     }
     
+    status = SecCodeCheckValidity(codeRef, kSecCSDefaultFlags, NULL);
+    require_noerr_action(status, done, os_log_error(AUTHD_LOG, "process: PID %d SecCodeCheckValidity failed with %d", proc->auditInfo.pid, (int)status));
+    
     status = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &code_info);
     require_noerr_action(status, done, os_log_debug(AUTHD_LOG, "process: PID %d SecCodeCopySigningInformation failed with %d", proc->auditInfo.pid, (int)status));
 
index 98fb9085770d6a5d540494df6247ef794a2cc999..22a1cdfe3340d5a75cf9195c8f7b645d3e659eb3 100644 (file)
@@ -219,6 +219,9 @@ static OSStatus osstatus_for_ctk_error(CFIndex ctkError) {
             return errSecUserCanceled;
         case kTKErrorCodeCorruptedData:
             return errSecDecode;
+        case kTKErrorCodeTokenNotFound:
+        case kTKErrorCodeObjectNotFound:
+            return errSecItemNotFound;
         default:
             return errSecInternal;
     }
@@ -1130,9 +1133,16 @@ bool SecItemResultProcess(CFDictionaryRef query, CFDictionaryRef auth_params, TK
         *result = CFArrayCreateMutableForCFTypes(NULL);
         for (i = 0; i < count; i++) {
             CFTypeRef ref;
-            require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result, i),
-                                                    token, query, auth_params, &ref, error), out);
-            if (ref != NULL) {
+            CFErrorRef localError = NULL;
+            bool prepared = SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result, i),
+                                                      token, query, auth_params, &ref, &localError);
+            if (!prepared) {
+                // TokenNotFound or TokenObjectNotFound will just not insert failing item into resulting array, other errors abort processing.
+                require_action_quiet(localError != NULL && CFEqual(CFErrorGetDomain(localError), CFSTR(kTKErrorDomain)) &&
+                                     (CFErrorGetCode(localError) == kTKErrorCodeTokenNotFound || CFErrorGetCode(localError) == kTKErrorCodeObjectNotFound), out,
+                                     CFErrorPropagate(localError, error));
+                CFReleaseNull(localError);
+            } else if (ref != NULL) {
                 CFArrayAppendValue((CFMutableArrayRef)*result, ref);
                 CFRelease(ref);
             }
@@ -1471,8 +1481,18 @@ bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *attribute
         // Prepare connection to target token if it is present.
         CFStringRef token_id = CFDictionaryGetValue(query->dictionary, kSecAttrTokenID);
         require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
-        if (secItemOperation != SecItemCopyMatching && token_id != NULL) {
-            require_quiet(CFAssignRetained(token, SecTokenCreate(token_id, &auth_params, error)), out);
+        if (secItemOperation != SecItemCopyMatching && token_id != NULL && !cf_bool_value(CFDictionaryGetValue(query->dictionary, kSecUseTokenRawItems))) {
+            CFErrorRef localError = NULL;
+            CFAssignRetained(token, SecTokenCreate(token_id, &auth_params, &localError));
+            if (token == NULL) {
+                require_action_quiet(secItemOperation == SecItemDelete &&
+                                     CFEqual(CFErrorGetDomain(localError), CFSTR(kTKErrorDomain)) &&
+                                     CFErrorGetCode(localError) == kTKErrorCodeTokenNotFound,
+                                     out, CFErrorPropagate(localError, error));
+
+                // In case that token cannot be found and deletion is required, just continue and delete item from keychain only.
+                CFReleaseNull(localError);
+            }
         }
 
         CFDictionaryRef attrs = (attributes != NULL) ? attributes->dictionary : NULL;
@@ -1941,8 +1961,14 @@ OSStatus SecItemDelete(CFDictionaryRef inQuery) {
 
                     // Delete item from the token.
                     CFDataRef object_id = CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey);
-                    require_action_quiet(TKTokenDeleteObject(token, object_id, error), out,
-                                         SecTokenProcessError(kAKSKeyOpDelete, token, object_id, error));
+                    CFErrorRef localError = NULL;
+                    if (!TKTokenDeleteObject(token, object_id, &localError)) {
+                        // Check whether object was not found; in this case, ignore the error.
+                        require_action_quiet(CFEqual(CFErrorGetDomain(localError), CFSTR(kTKErrorDomain)) &&
+                                             CFErrorGetCode(localError) == kTKErrorCodeObjectNotFound, out,
+                                             (CFErrorPropagate(localError, error), SecTokenProcessError(kAKSKeyOpDelete, token, object_id, error)));
+                        CFReleaseNull(localError);
+                    }
 
                     // Delete the item from the keychain.
                     require_quiet(SECURITYD_XPC(sec_item_delete, dict_client_to_error_request, item_query,
index df744076dced74b6bff6c67132cf4a98ff158434..1a8eb59e0098a2d27d3bc3e3a454d25f0ae159b0 100644 (file)
@@ -895,11 +895,8 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
             case kSecXPCOpRequestToJoinWithAnalytics:
                 if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
                     CFDataRef parentEvent = NULL;
-                    if(SecXPCDictionaryCopyDataOptional(event, kSecXPCKeySignInAnalytics, &parentEvent, &error)  && parentEvent != NULL){
-                        xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRequestToJoinCircleWithAnalytics_Server(parentEvent, &error));
-                    }else{
-                        xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRequestToJoinCircle_Server(&error));
-                    }
+                    SecXPCDictionaryCopyDataOptional(event, kSecXPCKeySignInAnalytics, &parentEvent, &error);
+                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRequestToJoinCircleWithAnalytics_Server(parentEvent, &error));
                     CFReleaseNull(parentEvent);
                 }
                 break;
index 9986b7cb2aa7df7ba549c15ba8e363ade9a6e687..216018f93d2865419decda179a08bc3a3dff2532 100644 (file)
@@ -34,6 +34,17 @@ void SecCreateSecuritydXPCServer(void);
 
 bool fill_security_client(SecurityClient * client, const uid_t uid, audit_token_t auditToken);
 CFArrayRef SecTaskCopyAccessGroups(SecTaskRef task);
+
+/*!
+ @function SecTaskIsEligiblePlatformBinary
+ @abstract Determine whether task belongs to valid platform binary and optionally has one of the allowed identifiers.
+ @param task The client task to be evaluated.
+ @param identifiers Optional array of codesigning identifiers of allowed callers. Pass NULL to permit any platform binary.
+ @result Client satisfies the criteria or not.
+ */
+bool SecTaskIsEligiblePlatformBinary(SecTaskRef task, CFArrayRef identifiers);
+
+// Testing support
 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups);
 
 #endif /* server_security_helpers_h */
index cd7f0e159a52165be176e68cf033725d40671326..b360fc43c789c0b18e3ebbfce459da0277db0137 100644 (file)
@@ -30,6 +30,8 @@
 #include <Security/SecTaskPriv.h>
 #include "ipc/securityd_client.h"
 #include <Security/SecEntitlements.h>
+#include <utilities/SecInternalReleasePriv.h>
+#include <sys/codesign.h>
 #include <Security/SecItem.h>
 #include "utilities/SecCFRelease.h"
 #include "utilities/SecCFWrappers.h"
@@ -66,6 +68,35 @@ device_is_multiuser(void)
 }
 #endif /* HAVE_MOBILE_KEYBAG_SUPPORT && TARGET_OS_IOS */
 
+static bool sanityCheckClientAccessGroups(SecurityClient* client) {
+    if (!client->accessGroups) {
+        return true;
+    }
+
+    CFRange range = { 0, CFArrayGetCount(client->accessGroups) };
+    if (!CFArrayContainsValue(client->accessGroups, range, CFSTR("*"))) {
+        return true;
+    }
+
+    CFMutableArrayRef allowedIdentifiers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+#if TARGET_OS_OSX
+    CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.keychainaccess"));
+    CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.KeychainMigrator"));
+#endif
+    if (SecIsInternalRelease()) {
+#if TARGET_OS_OSX
+        CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.security2"));
+#else
+        CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.security"));
+#endif
+    }
+
+    bool answer = SecTaskIsEligiblePlatformBinary(client->task, allowedIdentifiers);
+    CFReleaseNull(allowedIdentifiers);
+
+    return answer;
+}
+
 bool
 fill_security_client(SecurityClient * client, const uid_t uid, audit_token_t auditToken) {
     if(!client) {
@@ -141,7 +172,67 @@ fill_security_client(SecurityClient * client, const uid_t uid, audit_token_t aud
             client->allowSyncBubbleKeychain = SecTaskGetBooleanValueForEntitlement(client->task, kSecEntitlementPrivateKeychainSyncBubble);
         }
 #endif
+        if (!sanityCheckClientAccessGroups(client)) {
+            return false;
+        }
     }
     return true;
 }
 
+// Stolen and adapted from securityd_service
+bool SecTaskIsEligiblePlatformBinary(SecTaskRef task, CFArrayRef identifiers) {
+#if (DEBUG || RC_BUILDIT_YES)
+    secnotice("serverxpc", "Accepting client because debug");
+    return true;
+#else
+
+    if (task == NULL) {
+        secerror("serverxpc: Client task is null, cannot verify platformness");
+        return false;
+    }
+
+    uint32_t flags = SecTaskGetCodeSignStatus(task);
+    /* check if valid and platform binary, but not platform path */
+
+    if ((flags & (CS_VALID | CS_PLATFORM_BINARY | CS_PLATFORM_PATH)) != (CS_VALID | CS_PLATFORM_BINARY)) {
+        if (SecIsInternalRelease()) {
+            if ((flags & (CS_DEBUGGED | CS_PLATFORM_BINARY | CS_PLATFORM_PATH)) != (CS_DEBUGGED | CS_PLATFORM_BINARY)) {
+                secerror("serverxpc: client is not a platform binary: 0x%08x", flags);
+                return false;
+            }
+        } else {
+            secerror("serverxpc: client is not a platform binary: 0x%08x", flags);
+            return false;
+        }
+    }
+
+    CFStringRef signingIdentifier = SecTaskCopySigningIdentifier(task, NULL);
+    if (identifiers) {
+        if (signingIdentifier == NULL) {
+            secerror("serverxpc: client has no codesign identifier");
+            return false;
+        }
+
+        __block bool result = false;
+        CFArrayForEach(identifiers, ^(const void *value) {
+            if (CFEqual(value, signingIdentifier)) {
+                result = true;
+            }
+        });
+
+        if (result == true) {
+            secinfo("serverxpc", "client %@ is eligible platform binary", signingIdentifier);
+        } else {
+            secerror("serverxpc: client %@ is not eligible", signingIdentifier);
+        }
+
+        CFReleaseNull(signingIdentifier);
+        return result;
+    }
+
+    secinfo("serverxpc", "Client %@ is valid platform binary", signingIdentifier);
+    CFReleaseNull(signingIdentifier);
+    return true;
+
+#endif
+}
index f7674f30d0ee44dd0fa43a4970a636e015c91c4c..7d8881025fd3d822a43ad3461c2c632433d354a3 100644 (file)
@@ -105,12 +105,9 @@ void SOSTransportEachMessage(SOSAccount*  account, CFDictionaryRef updates, CFEr
 
 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status);
 SOSCCStatus SOSAccountGetSOSCCStatusFromString(CFStringRef status);
-bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error);
-bool SOSAccountJoinCirclesWithAnalytics(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error);
-bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error);
-bool SOSAccountJoinCirclesAfterRestoreWithAnalytics(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error);
-bool SOSAccountRemovePeersFromCircle(SOSAccount*  account, CFArrayRef peers, CFErrorRef* error);
-bool SOSAccountRemovePeersFromCircleWithAnalytics(SOSAccount*  account, CFArrayRef peers, NSData* parentEvent, CFErrorRef* error);
+bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error);
+bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error);
+bool SOSAccountRemovePeersFromCircle(SOSAccount*  account, CFArrayRef peers, NSData* parentEvent, CFErrorRef* error);
 bool SOSAccountBail(SOSAccount*  account, uint64_t limit_in_seconds, CFErrorRef* error);
 bool SOSAccountAcceptApplicants(SOSAccount*  account, CFArrayRef applicants, CFErrorRef* error);
 bool SOSAccountRejectApplicants(SOSAccount*  account, CFArrayRef applicants, CFErrorRef* error);
@@ -286,6 +283,8 @@ void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* p
 NSArray<NSDictionary *>* SOSAccountGetAllTLKs(void);
 CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountCopyiCloudIdentities(SOSAccount* account);
 
+bool SOSAccountEvaluateKeysAndCircle(SOSAccountTransaction *txn, CFErrorRef *block_error);
+
 __END_DECLS
 
 #endif /* !_SOSACCOUNT_H_ */
index b75b48cfbfe534b0ca3015369681b8e0ec966c83..9976962f2b52276a21a649838002367854edaf6f 100644 (file)
@@ -1135,14 +1135,18 @@ void SOSAccountPurgeIdentity(SOSAccount*  account) {
     }
 }
 
-bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error) {
+bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error) {
     SOSAccountTrustClassic *trust = account.trust;
     SOSFullPeerInfoRef identity = trust.fullPeerInfo;
     NSMutableSet* retirees = trust.retirees;
 
     NSError* localError = nil;
-    SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentData error:&localError];
+    SFSignInAnalytics* parent = nil;
     
+    if(parentData) {
+        parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentData error:&localError];
+    }
+
     SOSFullPeerInfoRef fpi = identity;
     if(!fpi) return false;
 
@@ -1150,10 +1154,17 @@ bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle
 
     bool retval = false;
 
-    SFSignInAnalytics *promoteToRetiredEvent = [parent newSubTaskForEvent:@"promoteToRetiredEvent"];
+    SFSignInAnalytics *promoteToRetiredEvent = nil;
+    
+    if(parent) {
+        promoteToRetiredEvent = [parent newSubTaskForEvent:@"promoteToRetiredEvent"];
+    }
+    
     SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &retiredError);
     if(retiredError){
-        [promoteToRetiredEvent logRecoverableError:(__bridge NSError*)retiredError];
+        if(promoteToRetiredEvent) {
+            [promoteToRetiredEvent logRecoverableError:(__bridge NSError*)retiredError];
+        }
         secerror("SOSFullPeerInfoPromoteToRetiredAndCopy error: %@", retiredError);
         if(error){
             *error = retiredError;
@@ -1161,7 +1172,9 @@ bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle
             CFReleaseNull(retiredError);
         }
     }
-    [promoteToRetiredEvent stopWithAttributes:nil];
+    if(promoteToRetiredEvent) {
+        [promoteToRetiredEvent stopWithAttributes:nil];
+    }
 
     if (!retire_peer) {
         secerror("Create ticket failed for peer %@: %@", fpi, localError);
@@ -1173,11 +1186,17 @@ bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle
         } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
             if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
                 CFErrorRef cleanupError = NULL;
-                SFSignInAnalytics *cleanupEvent = [parent newSubTaskForEvent:@"cleanupAfterPeerEvent"];
+                SFSignInAnalytics *cleanupEvent = nil;
+
+                if(parent) {
+                    cleanupEvent = [parent newSubTaskForEvent:@"cleanupAfterPeerEvent"];
+                }
                 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
                     secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
                 }
-                [cleanupEvent stopWithAttributes:nil];
+                if(cleanupEvent) {
+                    [cleanupEvent stopWithAttributes:nil];
+                }
                 CFReleaseSafe(cleanupError);
             }
         }
@@ -1189,82 +1208,48 @@ bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle
 
         // Write retirement to Transport
         CFErrorRef postError = NULL;
-        SFSignInAnalytics *postRestirementEvent = [parent newSubTaskForEvent:@"postRestirementEvent"];
+        SFSignInAnalytics *postRetirementEvent = nil;
+        
+        if(parent) {
+            postRetirementEvent = [parent newSubTaskForEvent:@"postRestirementEvent"];
+        }
+        
         if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
-            [postRestirementEvent logRecoverableError:(__bridge NSError*)postError];
+            if(postRetirementEvent) {
+                [postRetirementEvent logRecoverableError:(__bridge NSError*)postError];
+            }
             secwarning("Couldn't post retirement (%@)", postError);
         }
-        [postRestirementEvent stopWithAttributes:nil];
-
-        SFSignInAnalytics *flushChangesEvent = [parent newSubTaskForEvent:@"flushChangesEvent"];
-
-        if(![account.circle_transport flushChanges:&postError]){
-            [flushChangesEvent logRecoverableError:(__bridge NSError*)postError];
-            secwarning("Couldn't flush retirement data (%@)", postError);
+        if(postRetirementEvent) {
+            [postRetirementEvent stopWithAttributes:nil];
         }
-        [flushChangesEvent stopWithAttributes:nil];
-        CFReleaseNull(postError);
-    }
-    SFSignInAnalytics *purgeIdentityEvent = [parent newSubTaskForEvent:@"purgeIdentityEvent"];
-    SOSAccountPurgeIdentity(account);
-    [purgeIdentityEvent stopWithAttributes:nil];
-    retval = true;
 
-    CFReleaseNull(retire_peer);
-    return retval;
-}
-
-bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error) {
-    SOSAccountTrustClassic *trust = account.trust;
-    SOSFullPeerInfoRef identity = trust.fullPeerInfo;
-    NSMutableSet* retirees = trust.retirees;
-
-    SOSFullPeerInfoRef fpi = identity;
-    if(!fpi) return false;
-
-       CFErrorRef localError = NULL;
-
-    bool retval = false;
-
-    SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
-    if (!retire_peer) {
-        secerror("Create ticket failed for peer %@: %@", fpi, localError);
-    } else {
-        // See if we need to repost the circle we could either be an applicant or a peer already in the circle
-        if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
-            // Remove our application if we have one.
-            SOSCircleWithdrawRequest(circle, retire_peer, NULL);
-        } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
-            if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
-                CFErrorRef cleanupError = NULL;
-                if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
-                    secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
-                }
-                CFReleaseSafe(cleanupError);
-            }
+        SFSignInAnalytics *flushChangesEvent = nil;
+        if(parent) {
+            flushChangesEvent = [parent newSubTaskForEvent:@"flushChangesEvent"];
         }
 
-        // Store the retirement record locally.
-        CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
-
-        trust.retirees = retirees;
-        
-        // Write retirement to Transport
-        CFErrorRef postError = NULL;
-        if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
-            secwarning("Couldn't post retirement (%@)", postError);
-        }
         if(![account.circle_transport flushChanges:&postError]){
+            if(flushChangesEvent) {
+                [flushChangesEvent logRecoverableError:(__bridge NSError*)postError];
+            }
             secwarning("Couldn't flush retirement data (%@)", postError);
         }
+        if(flushChangesEvent) {
+            [flushChangesEvent stopWithAttributes:nil];
+        }
         CFReleaseNull(postError);
     }
-
+    SFSignInAnalytics *purgeIdentityEvent = nil;
+    if(parent) {
+        purgeIdentityEvent = [parent newSubTaskForEvent:@"purgeIdentityEvent"];
+    }
     SOSAccountPurgeIdentity(account);
-
+    if(purgeIdentityEvent) {
+        [purgeIdentityEvent stopWithAttributes:nil];
+    }
     retval = true;
 
-    CFReleaseNull(localError);
     CFReleaseNull(retire_peer);
     return retval;
 }
@@ -1451,81 +1436,43 @@ CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransaction* txn, CFErrorRef
 // MARK: Joining
 //
 
-static bool SOSAccountJoinCircleWithAnalytics(SOSAccountTransaction* aTxn, SecKeyRef user_key,
-                                 bool use_cloud_peer, NSData* parentEvent, CFErrorRef* error) {
+static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key, bool use_cloud_peer, NSData* parentEvent, CFErrorRef* error) {
     SOSAccount* account = aTxn.account;
     SOSAccountTrustClassic *trust = account.trust;
     __block bool result = false;
     __block SOSFullPeerInfoRef cloud_full_peer = NULL;
     SFSignInAnalytics *ensureFullPeerAvailableEvent = nil;
     NSError* localError = nil;
-    SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
-
-    require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
-    ensureFullPeerAvailableEvent = [parent newSubTaskForEvent:@"ensureFullPeerAvailableEvent"];
-    require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
-    [ensureFullPeerAvailableEvent stopWithAttributes:nil];
+    SFSignInAnalytics* parent = nil;
     
-    SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
-    if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
-        secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
-        // this also clears initial sync data
-        result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
-    } else {
-        SOSAccountInitializeInitialSync(account);
-        if (use_cloud_peer) {
-            cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
-        }
-        SFSignInAnalytics *acceptApplicantEvent = [parent newSubTaskForEvent:@"acceptApplicantEvent"];
-        [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
-            result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
-            result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
-            trust.departureCode = kSOSNeverLeftCircle;
-            if(result && cloud_full_peer) {
-                CFErrorRef localError = NULL;
-                CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
-                require_quiet(cloudid, finish);
-                require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
-                require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
-            finish:
-                if (localError){
-                    [acceptApplicantEvent logRecoverableError:(__bridge NSError *)(localError)];
-                    secerror("Failed to join with cloud identity: %@", localError);
-                    CFReleaseNull(localError);
-                }
-            }
-            return result;
-        }];
-        [acceptApplicantEvent stopWithAttributes:nil];
-        if (use_cloud_peer) {
-            SFSignInAnalytics *updateOutOfDateSyncViewsEvent = [acceptApplicantEvent newSubTaskForEvent:@"updateOutOfDateSyncViewsEvent"];
-            SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
-            [updateOutOfDateSyncViewsEvent stopWithAttributes:nil];
-        }
+    if(parentEvent) {
+        parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
     }
-fail:
-    CFReleaseNull(cloud_full_peer);
-    return result;
-}
 
-static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key,
-                                 bool use_cloud_peer, CFErrorRef* error) {
-    SOSAccount* account = aTxn.account;
-    SOSAccountTrustClassic *trust = account.trust;
-    __block bool result = false;
-    __block SOSFullPeerInfoRef cloud_full_peer = NULL;
     require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
+    if(parent) {
+        ensureFullPeerAvailableEvent = [parent newSubTaskForEvent:@"ensureFullPeerAvailableEvent"];
+    }
     require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
+    if(ensureFullPeerAvailableEvent) {
+        [ensureFullPeerAvailableEvent stopWithAttributes:nil];
+    }
+    
     SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
     if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
         secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
         // this also clears initial sync data
         result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
     } else {
+        SFSignInAnalytics *acceptApplicantEvent = nil;
+
         SOSAccountInitializeInitialSync(account);
         if (use_cloud_peer) {
             cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
         }
+        if(parent) {
+            acceptApplicantEvent = [parent newSubTaskForEvent:@"acceptApplicantEvent"];
+        }
         [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
             result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
             result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
@@ -1538,14 +1485,24 @@ static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key
                 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
             finish:
                 if (localError){
+                    if(acceptApplicantEvent) {
+                        [acceptApplicantEvent logRecoverableError:(__bridge NSError *)(localError)];
+                    }
                     secerror("Failed to join with cloud identity: %@", localError);
                     CFReleaseNull(localError);
                 }
             }
             return result;
         }];
+        if(acceptApplicantEvent) {
+            [acceptApplicantEvent stopWithAttributes:nil];
+        }
         if (use_cloud_peer) {
             SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
+            if(acceptApplicantEvent) {
+                SFSignInAnalytics *updateOutOfDateSyncViewsEvent = [acceptApplicantEvent newSubTaskForEvent:@"updateOutOfDateSyncViewsEvent"];
+                [updateOutOfDateSyncViewsEvent stopWithAttributes:nil];
+            }
         }
     }
 fail:
@@ -1553,7 +1510,7 @@ fail:
     return result;
 }
 
-static bool SOSAccountJoinCirclesWithAnalytics_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, NSData* parentEvent, CFErrorRef* error) {
+static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, NSData* parentEvent, CFErrorRef* error) {
     SOSAccount* account = aTxn.account;
     SOSAccountTrustClassic *trust = account.trust;
     bool success = false;
@@ -1561,7 +1518,21 @@ static bool SOSAccountJoinCirclesWithAnalytics_internal(SOSAccountTransaction* a
     SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
     require_quiet(user_key, done); // Fail if we don't get one.
 
-    require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
+    if(!trust.trustedCircle || SOSCircleCountPeers(trust.trustedCircle) == 0 ) {
+        secnotice("resetToOffering", "Resetting circle to offering because it's empty and we're joining");
+        return [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
+    }
+    
+    if(SOSCircleHasPeerWithID(trust.trustedCircle, (__bridge CFStringRef)(account.peerID), NULL)) {
+        // We shouldn't be at this point if we're already in circle.
+        secnotice("circleops", "attempt to join a circle we're in - continuing.");
+        return true; // let things above us think we're in circle - because we are.
+    }
+    
+    if(!SOSCircleVerify(trust.trustedCircle, account.accountKey, NULL)) {
+        secnotice("resetToOffering", "Resetting circle to offering since we are new and it doesn't verify with current userKey");
+        return [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
+    }
 
     if (trust.fullPeerInfo != NULL) {
         SOSPeerInfoRef myPeer = trust.peerInfo;
@@ -1577,7 +1548,7 @@ static bool SOSAccountJoinCirclesWithAnalytics_internal(SOSAccountTransaction* a
         }
     }
 
-    success = SOSAccountJoinCircleWithAnalytics(aTxn, user_key, use_cloud_identity, parentEvent, error);
+    success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, parentEvent, error);
 
     require_quiet(success, done);
 
@@ -1587,127 +1558,26 @@ done:
     return success;
 }
 
-static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, CFErrorRef* error) {
-    SOSAccount* account = aTxn.account;
-    SOSAccountTrustClassic *trust = account.trust;
-    bool success = false;
-
-    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
-    require_quiet(user_key, done); // Fail if we don't get one.
-
-    require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
-    
-    if (trust.fullPeerInfo != NULL) {
-        SOSPeerInfoRef myPeer = trust.peerInfo;
-        success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
-        require_quiet(!success, done);
-
-        SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
-
-        if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
-               secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
-            
-            trust.fullPeerInfo = NULL;
-        }
-    }
-    
-    success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
-
-    require_quiet(success, done);
-       
-    trust.departureCode = kSOSNeverLeftCircle;
-
-done:
-    return success;
-}
-
-bool SOSAccountJoinCirclesWithAnalytics(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error) {
-    secnotice("circleOps", "Normal path circle join (SOSAccountJoinCirclesWithAnalytics)");
-    return SOSAccountJoinCirclesWithAnalytics_internal(aTxn, false, parentEvent, error);
+bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error) {
+    secnotice("circleOps", "Normal path circle join (SOSAccountJoinCircles)");
+    return SOSAccountJoinCircles_internal(aTxn, false, parentEvent, error);
 }
 
-bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error) {
-       secnotice("circleOps", "Normal path circle join (SOSAccountJoinCircles)");
-    return SOSAccountJoinCircles_internal(aTxn, false, error);
-}
-
-bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error) {
-       secnotice("circleOps", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
-    return SOSAccountJoinCircles_internal(aTxn, true, error);
-}
-
-bool SOSAccountJoinCirclesAfterRestoreWithAnalytics(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error) {
+bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error) {
     secnotice("circleOps", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
-    return SOSAccountJoinCirclesWithAnalytics_internal(aTxn, true, parentEvent, error);
+    return SOSAccountJoinCircles_internal(aTxn, true, parentEvent, error);
 }
 
-bool SOSAccountRemovePeersFromCircle(SOSAccount*  account, CFArrayRef peers, CFErrorRef* error)
+bool SOSAccountRemovePeersFromCircle(SOSAccount*  account, CFArrayRef peers, NSData* parentEvent, CFErrorRef* error)
 {
-    bool result = false;
-    CFMutableSetRef peersToRemove = NULL;
-    SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
-    if(!user_key){
-        secnotice("circleOps", "Can't remove without userKey");
-        return result;
-    }
-    SOSFullPeerInfoRef me_full = account.fullPeerInfo;
-    SOSPeerInfoRef me = account.peerInfo;
-    if(!(me_full && me))
-    {
-        secnotice("circleOps", "Can't remove without being active peer");
-        SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
-        return result;
-    }
-    
-    result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
-
-    peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
-    if(!peersToRemove)
-    {
-        CFReleaseNull(peersToRemove);
-        secnotice("circleOps", "No peerSet to remove");
-        return result;
-    }
-
-    // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
-    bool leaveCircle = CFSetContainsValue(peersToRemove, me);
-    CFSetRemoveValue(peersToRemove, me);
-
-    result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
-        bool success = false;
-
-        if(CFSetGetCount(peersToRemove) != 0) {
-            require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
-            success = SOSAccountGenerationSignatureUpdate(account, error);
-        } else success = true;
 
-        if (success && leaveCircle) {
-            secnotice("circleOps", "Leaving circle by client request (SOSAccountRemovePeersFromCircle)");
-            success = sosAccountLeaveCircle(account, circle, error);
-        }
-
-    done:
-        return success;
-
-    }];
+    NSError* localError = nil;
+    SFSignInAnalytics* parent = nil;
     
-    if(result) {
-        CFStringSetPerformWithDescription(peersToRemove, ^(CFStringRef description) {
-            secnotice("circleOps", "Removed Peers from circle %@", description);
-        });
+    if(parentEvent) {
+        parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
     }
 
-    CFReleaseNull(peersToRemove);
-    return result;
-}
-
-
-bool SOSAccountRemovePeersFromCircleWithAnalytics(SOSAccount*  account, CFArrayRef peers, NSData* parentEvent, CFErrorRef* error)
-{
-
-    NSError* localError = nil;
-    SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
-
     bool result = false;
     CFMutableSetRef peersToRemove = NULL;
     SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
@@ -1743,18 +1613,27 @@ bool SOSAccountRemovePeersFromCircleWithAnalytics(SOSAccount*  account, CFArrayR
 
         if(CFSetGetCount(peersToRemove) != 0) {
             require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
-            SFSignInAnalytics *generationSignatureUpdateEvent = [parent newSubTaskForEvent:@"generationSignatureUpdateEvent"];
+            
+            SFSignInAnalytics *generationSignatureUpdateEvent = nil;
+            if(parent) {
+                generationSignatureUpdateEvent = [parent newSubTaskForEvent:@"generationSignatureUpdateEvent"];
+            }
+            
             success = SOSAccountGenerationSignatureUpdate(account, error);
             if(error && *error){
                 NSError* signatureUpdateError = (__bridge NSError*)*error;
-                [generationSignatureUpdateEvent logUnrecoverableError:signatureUpdateError];
+                if(generationSignatureUpdateEvent) {
+                    [generationSignatureUpdateEvent logUnrecoverableError:signatureUpdateError];
+                }
+            }
+            if(generationSignatureUpdateEvent) {
+                [generationSignatureUpdateEvent stopWithAttributes:nil];
             }
-            [generationSignatureUpdateEvent stopWithAttributes:nil];
         } else success = true;
 
         if (success && leaveCircle) {
             secnotice("circleOps", "Leaving circle by client request (SOSAccountRemovePeersFromCircle)");
-            success = sosAccountLeaveCircleWithAnalytics(account, circle, parentEvent, error);
+            success = sosAccountLeaveCircle(account, circle, parentEvent, error);
         }
 
     done:
@@ -1782,7 +1661,7 @@ bool SOSAccountBail(SOSAccount*  account, uint64_t limit_in_seconds, CFErrorRef*
     dispatch_group_async(group, queue, ^{
         [trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
             secnotice("circleOps", "Leaving circle by client request (Bail)");
-            return sosAccountLeaveCircle(account, circle, error);
+            return sosAccountLeaveCircle(account, circle, nil, error);
         }];
     });
     dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
index 26e84b444055850d5b17c424e7d2e01ca28878a2..192548db57f666694c0bc5da9898b6fe011f4d9f 100644 (file)
@@ -11,6 +11,8 @@
 #import "keychain/SecureObjectSync/SOSTransportCircleCK.h"
 #import "keychain/SecureObjectSync/SOSAccountTrust.h"
 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
+#import "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
+#import "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
 
 #include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
 #include "SOSCloudKeychainClient.h"
@@ -42,3 +44,10 @@ fail:
     return circle;
 }
 
+bool SOSAccountEvaluateKeysAndCircle(SOSAccountTransaction *txn, CFErrorRef *error) {
+    // if the userKey signature on the circle doesn't work with the new userkey
+    if([txn.account.trust isInCircleOnly:nil]) {
+        return SOSAccountGenerationSignatureUpdate(txn.account, error);
+    }
+    return true;
+}
index 31f28a5cd5ca4e23f3cf5d43e7b506bd7c216218..efe750702edf085e9865f36b33aad181d03593b7 100644 (file)
@@ -29,6 +29,7 @@
 #include "SOSTransport.h"
 #import "keychain/SecureObjectSync/SOSAccountTrust.h"
 #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
+#import "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
 
 #define kPublicKeyAvailable "com.apple.security.publickeyavailable"
 //
index 249a8e00061f829ff46619c11efdd67bd9eecceb..d90657dbe0e97db5561649366cb974c35e86a37c 100644 (file)
@@ -269,8 +269,7 @@ void SOSAccountSetLastDepartureReason(SOSAccount* account, enum DepartureReason
 void SOSAccountSetUserPublicTrustedForTesting(SOSAccount* account);
 
 void SOSAccountPurgeIdentity(SOSAccount*);
-bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error);
-bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error);
+bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error);
 
 bool SOSAccountForEachRing(SOSAccount* account, SOSRingRef (^action)(CFStringRef name, SOSRingRef ring));
 bool SOSAccountUpdateBackUp(SOSAccount* account, CFStringRef viewname, CFErrorRef *error);
index d118ec544e85474e3b7c1e55ebf7aa1a67e6cc29..ec3b56640c1640de0508ff12bc20e1e505e15405 100644 (file)
@@ -402,7 +402,7 @@ static bool SOSCircleHasUpdatedPeerInfoWithOctagonKey(SOSCircleRef oldCircle, SO
             secnotice("account", "Key state: accountKey %@, previousAccountKey %@, old_circle_key %@",
                       account.accountKey, account.previousAccountKey, old_circle_key);
             
-            if (sosAccountLeaveCircle(account, newCircle, error)) {
+            if (sosAccountLeaveCircle(account, newCircle, nil, error)) {
                 secnotice("circleOps", "Leaving circle by newcircle state");
                 circleToPush = newCircle;
             } else {
@@ -701,7 +701,7 @@ fail:
     bool result = true;
     secnotice("circleOps", "leaveCircleWithAccount: Leaving circle by client request");
     result &= [self modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
-        return sosAccountLeaveCircleWithAnalytics(account, circle, parentEvent, error);
+        return sosAccountLeaveCircle(account, circle, parentEvent, error);
     }];
 
     self.departureCode = kSOSWithdrewMembership;
@@ -714,7 +714,7 @@ fail:
     bool result = true;
     secnotice("circleOps", "Leaving circle by client request");
     result &= [self modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
-        return sosAccountLeaveCircle(account, circle, error);
+        return sosAccountLeaveCircle(account, circle, nil, error);
     }];
     account.backup_key = nil;
     self.departureCode = kSOSWithdrewMembership;
index f953e859d816c67fc1cf375e422807e8a5794924..b80f1e0dc85b6f7ca156875eaa1011e1dc82d10a 100644 (file)
@@ -1320,7 +1320,9 @@ void SOSCircleForEachApplicant(SOSCircleRef circle, void (^action)(SOSPeerInfoRe
 
 bool SOSCircleHasPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
     SOSCircleAssertStable(circle);
-
+    if(!peerid) {
+        return false;
+    }
     SOSPeerInfoRef found = asSOSPeerInfo(CFSetGetValue(circle->peers, peerid));
     return found && !isHiddenPeer(found);
 }
index b5ac7067ccdd4672124b24076aebab86180f0b03..ad1e321b9c9d6299e45aea1edd26eb7a73aabe22 100644 (file)
@@ -906,7 +906,6 @@ class Container: NSObject {
     func onQueueDetermineLocalTrustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
         let viablePeerCountsByModelID = self.model.viablePeerCountsByModelID()
         let peerCountsByMachineID = self.model.peerCountsByMachineID()
-
         if let egoPeerID = self.containerMO.egoPeerID {
             var status = self.model.statusOfPeer(withID: egoPeerID)
             var isExcluded: Bool = (status == .excluded)
index be6ecf968e25bdb8331bb8dfe4187657f8a5364b..a4e5c8fcf7b35ccae2a3149d908d021fac12ccf6 100644 (file)
@@ -2557,12 +2557,15 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
             else if((status&TPPeerStatusFullyReciprocated) == TPPeerStatusFullyReciprocated){
                 trustStatus = CliqueStatusIn;
             }
-            else if((status&TPPeerStatusUnknown) == TPPeerStatusUnknown){
-                trustStatus = CliqueStatusAbsent;
-            }
             else if ((status&TPPeerStatusSelfTrust) == TPPeerStatusSelfTrust) {
                 trustStatus = CliqueStatusIn;
             }
+            else if ((status&TPPeerStatusIgnored) == TPPeerStatusIgnored) {
+                trustStatus = CliqueStatusNotIn;
+            }
+            else if((status&TPPeerStatusUnknown) == TPPeerStatusUnknown){
+                trustStatus = CliqueStatusAbsent;
+            }
             else {
                 secnotice("octagon", "TPPeerStatus is empty");
                 trustStatus = CliqueStatusAbsent;
index 3bc43ec728c0c144dbc3f3e68b7dac8f12f8cd00..db3fb52f508579ea791bfeab3ba445b3d18aea17 100644 (file)
@@ -707,6 +707,94 @@ class OctagonResetTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
     }
+
+    func testCliqueResetProtectedDataHandlingInMultiPeerCircle() throws {
+        OctagonSetSOSFeatureEnabled(false)
+        self.startCKAccountStatusMock()
+
+        var firstCliqueIdentifier: String?
+
+        let clique: OTClique
+        let cliqueContextConfiguration = OTConfigurationContext()
+        cliqueContextConfiguration.context = OTDefaultContext
+        cliqueContextConfiguration.dsid = "13453464"
+        cliqueContextConfiguration.altDSID = self.mockAuthKit.altDSID!
+        cliqueContextConfiguration.authenticationAppleID = "appleID"
+        cliqueContextConfiguration.passwordEquivalentToken = "petpetpetpetpet"
+        cliqueContextConfiguration.otControl = self.otControl
+        cliqueContextConfiguration.ckksControl = self.ckksControl
+        cliqueContextConfiguration.sbd = OTMockSecureBackup(bottleID: nil, entropy: nil)
+        do {
+            clique = try OTClique.newFriends(withContextData: cliqueContextConfiguration, resetReason: .testGenerated)
+            XCTAssertNotNil(clique, "Clique should not be nil")
+            XCTAssertNotNil(clique.cliqueMemberIdentifier, "Should have a member identifier after a clique newFriends call")
+            firstCliqueIdentifier = clique.cliqueMemberIdentifier
+        } catch {
+            XCTFail("Shouldn't have errored making new friends: \(error)")
+            throw error
+        }
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+
+        let entropy = try self.loadSecret(label: clique.cliqueMemberIdentifier!)
+        XCTAssertNotNil(entropy, "entropy should not be nil")
+
+        let bottleJoinerContextID = "bottleJoiner"
+        let joinerContext = self.manager.context(forContainerName: OTCKContainerName, contextID: bottleJoinerContextID)
+
+        let bottle = self.fakeCuttlefishServer.state.bottles[0]
+
+        joinerContext.startOctagonStateMachine()
+        self.startCKAccountStatusMock()
+        self.assertEnters(context: joinerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        // Before you call joinWithBottle, you need to call fetchViableBottles.
+        let fetchViableExpectation = self.expectation(description: "fetchViableBottles callback occurs")
+        joinerContext.rpcFetchAllViableBottles { viable, _, error in
+            XCTAssertNil(error, "should be no error fetching viable bottles")
+            XCTAssert(viable?.contains(bottle.bottleID) ?? false, "The bottle we're about to restore should be viable")
+            fetchViableExpectation.fulfill()
+        }
+        self.wait(for: [fetchViableExpectation], timeout: 10)
+
+        let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
+        joinerContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.mockAuthKit.altDSID!) { error in
+            XCTAssertNil(error, "error should be nil")
+            joinWithBottleExpectation.fulfill()
+        }
+
+        self.wait(for: [joinWithBottleExpectation], timeout: 10)
+
+        self.assertEnters(context: joinerContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context:joinerContext)
+
+        self.silentZoneDeletesAllowed = true
+
+        let newClique: OTClique
+        do {
+            newClique = try OTClique.resetProtectedData(cliqueContextConfiguration)
+            XCTAssertNotEqual(newClique.cliqueMemberIdentifier, firstCliqueIdentifier, "clique identifiers should be different")
+        } catch {
+            XCTFail("Shouldn't have errored resetting everything: \(error)")
+            throw error
+        }
+        XCTAssertNotNil(newClique, "newClique should not be nil")
+
+        self.sendContainerChangeWaitForFetchForStates(context: joinerContext, states: [OctagonStateUntrusted])
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateReady])
+        self.assertConsidersSelfTrusted(context:self.cuttlefishContext)
+
+        let statusExpectation = self.expectation(description: "status callback occurs")
+        let configuration = OTOperationConfiguration()
+
+        joinerContext.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
+            XCTAssertEqual(.notIn, egoStatus, "cliqueStatus should be 'Not In'")
+            statusExpectation.fulfill()
+        }
+        self.wait(for: [statusExpectation], timeout: 10)
+
+    }
 }
 
 #endif // OCTAGON
index 413b75cb37cdc4af2de446b13f760cfd080bbfc7..d3f6fd16ed955a302853a82395ca1c75dae012c6 100644 (file)
@@ -62,7 +62,7 @@ static inline bool SOSAccountJoinCirclesAfterRestore_wTxn(SOSAccount* acct, CFEr
 {
     __block bool result = false;
     [acct performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
-        result = SOSAccountJoinCirclesAfterRestore(txn, error);
+        result = SOSAccountJoinCirclesAfterRestore(txn, nil, error);
     }];
     return result;
 }
@@ -71,7 +71,7 @@ static inline bool SOSAccountJoinCircles_wTxn(SOSAccount* acct, CFErrorRef* erro
 {
     __block bool result = false;
     [acct performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
-        result = SOSAccountJoinCircles(txn, error);
+        result = SOSAccountJoinCircles(txn, nil, error);
     }];
     return result;
 }
index 645577416de153306f9d4f6c6f0f062327a5734a..b23910980af23afe71b2be431f8d34203242a1d3 100644 (file)
@@ -96,6 +96,8 @@ static void tests(void)
     ok(SOSAccountJoinCircles_wTxn(alice_account, &error), "Join circle: %@", error);
     ok(SOSAccountCheckHasBeenInSync_wTxn(alice_account), "Alice account initial sync done");
 
+    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
+
     ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
     CFReleaseNull(error);
     CFReleaseNull(cfpassword);
@@ -105,7 +107,7 @@ static void tests(void)
     ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
     CFReleaseNull(error);
     
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 4, "updates");
+    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
     
     {
         CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
index d8035f97ec97a2382e58d73d9c9678fed8522a93..b11e1d31713b9cc5a8c9774b6001fd3dcb41c318 100644 (file)
@@ -325,7 +325,7 @@ static void tests(void)
                                                          carol_account.peerInfo,
                                                          NULL);
 
-    ok(SOSAccountRemovePeersFromCircle(bob_account, peers_to_remove_array, NULL));
+    ok(SOSAccountRemovePeersFromCircle(bob_account, peers_to_remove_array, nil, NULL));
 
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, carol_account, NULL), 4, "Remove peers");
 
index 3135d5b768949729b7e83b9bc90a2c1e6d918f72..db6ff25c84880e93738310ff512b549c6813822a 100644 (file)
@@ -59,6 +59,15 @@ static bool AssertCreds(SOSAccount* account,CFStringRef acct_name, CFDataRef pas
     return retval;
 }
 
+static inline bool SOSAccountEvaluateKeysAndCircle_wTxn(SOSAccount* acct, CFErrorRef* error)
+{
+    __block bool result = false;
+    [acct performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        result = SOSAccountEvaluateKeysAndCircle(txn, NULL);
+    }];
+    return result;
+}
+
 static bool ResetToOffering(SOSAccount* account) {
     CFErrorRef error = NULL;
     bool retval;
@@ -213,11 +222,27 @@ static void tests(void)
     is(countPeers(alice_account), 3, "There are three peers - Alice, Carol, Bob");
     is(countActivePeers(alice_account), 4, "There are four active peers - bob, alice, carol and iCloud");
     is(countActiveValidPeers(alice_account), 3, "There are three active valid peers - alice, bob, and icloud");
+
+    /* Change Password 4 - new peer changes the password and joins ----------------------------------------------------*/
+    CFReleaseNull(cfnewpassword);
+    cfnewpassword = CFDataCreate(NULL, (uint8_t *) "dodododod", 10);
+
+    SOSAccount* david_account = CreateAccountForLocalChanges(CFSTR("David"), CFSTR("TestSource"));
+    ok(AssertCreds(david_account , cfaccount, cfnewpassword), "Credential resetting for David");
+    is(ProcessChangesUntilNoChange(changes, david_account, NULL), 2, "updates");
+    is(countPeers(david_account), 3, "Still 3 peers");
+    
     
+    ok(JoinCircle(david_account), "David Applies");
+    is(ProcessChangesUntilNoChange(changes, david_account, NULL), 2, "updates");
+    is(countPeers(david_account), 1, "Only David is in circle");
+    
+
     CFReleaseNull(cfnewpassword);
     alice_account = nil;
     bob_account = nil;
     carol_account = nil;
+    david_account = nil;
     SOSTestCleanup();
 }
 
index 71677a8a45a352d8f592044fed59d8d0548d1dec..ae3a447d29043fb64a46874f18c3a297384524d7 100644 (file)
@@ -951,47 +951,16 @@ done:
     return result;
 }
 
-static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error) {
-    secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid, user_label);
-
-    OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameAssertUserCredentialsAndOptionalDSID);
-
-    bool result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
-            SOSAccountAssertDSID(txn.account, dsid);
-        }
-        return true;
-    });
-
-    require_quiet(result, done);
-
-    require_quiet(SyncKVSAndWait(error), done); // Make sure we've seen what the server has
-    require_quiet(Flush(error), done);          // And processed it already...before asserting
-
-    result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *block_error) {
-        return SOSAccountAssertUserCredentials(txn.account, user_label, user_password, block_error);
-    });
-
-    require_quiet(result, done);
-    require_quiet(Flush(error), done); // Process any incoming information..circles et.al. before fixing our signature
-
-    result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        return SOSAccountGenerationSignatureUpdate(txn.account, error);
-    });
-
-    secnotice("updates", "Complete credentials and dsid (%@) for %@: %d %@",
-              dsid, user_label, result, error ? *error : NULL);
-done:
-    OctagonSignpostEnd(signPost, SOSSignpostNameAssertUserCredentialsAndOptionalDSID, OctagonSignpostNumber1(SOSSignpostNameAssertUserCredentialsAndOptionalDSID), (int)result);
-    return result;
-}
-
-static bool SOSCCAssertUserCredentialsAndOptionalDSIDWithAnalytics(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, NSData* parentEvent, CFErrorRef *error) {
+static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, NSData* parentEvent, CFErrorRef *error) {
     secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid, user_label);
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameAssertUserCredentialsAndOptionalDSID);
 
     NSError* localError = nil;
-    SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
+    SFSignInAnalytics* parent = nil;
+
+    if(parentEvent) {
+        parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
+    }
 
     SFSignInAnalytics *syncAndWaitEvent = nil;
     SFSignInAnalytics *flushEvent = nil;
@@ -1006,29 +975,46 @@ static bool SOSCCAssertUserCredentialsAndOptionalDSIDWithAnalytics(CFStringRef u
     });
 
     require_quiet(result, done);
-    syncAndWaitEvent = [parent newSubTaskForEvent:@"syncAndWaitEvent"];
+
+    if(parent) {
+        syncAndWaitEvent = [parent newSubTaskForEvent:@"syncAndWaitEvent"];
+    }
     require_quiet(SyncKVSAndWait(error), done); // Make sure we've seen what the server has
-    [syncAndWaitEvent stopWithAttributes:nil];
+    if(syncAndWaitEvent) {
+        [syncAndWaitEvent stopWithAttributes:nil];
+    }
 
-    flushEvent = [parent newSubTaskForEvent:@"flushEvent"];
+    if(parent) {
+        flushEvent = [parent newSubTaskForEvent:@"flushEvent"];
+    }
     require_quiet(Flush(error), done);          // And processed it already...before asserting
-    [flushEvent stopWithAttributes:nil];
+    if(flushEvent) {
+        [flushEvent stopWithAttributes:nil];
+    }
 
     result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *block_error) {
         return SOSAccountAssertUserCredentials(txn.account, user_label, user_password, block_error);
     });
 
     require_quiet(result, done);
-    secondFlushEvent = [parent newSubTaskForEvent:@"secondFlushEvent"];
+    if(parent) {
+        secondFlushEvent = [parent newSubTaskForEvent:@"secondFlushEvent"];
+    }
     require_quiet(Flush(error), done); // Process any incoming information..circles et.al. before fixing our signature
-    [secondFlushEvent stopWithAttributes:nil];
+    if(secondFlushEvent) {
+        [secondFlushEvent stopWithAttributes:nil];
+    }
 
-    generationSignatureUpdateEvent = [parent newSubTaskForEvent:@"generationSignatureUpdateEvent"];
+    if(parent) {
+        generationSignatureUpdateEvent = [parent newSubTaskForEvent:@"generationSignatureUpdateEvent"];
+    }
     result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
         return SOSAccountGenerationSignatureUpdate(txn.account, error);
     });
-    [generationSignatureUpdateEvent stopWithAttributes:nil];
 
+    if(generationSignatureUpdateEvent) {
+        [generationSignatureUpdateEvent stopWithAttributes:nil];
+    }
 
 done:
     if(syncAndWaitEvent){
@@ -1054,16 +1040,17 @@ done:
 bool SOSCCSetUserCredentialsAndDSID_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error)
 {
     // TODO: Return error if DSID is NULL to insist our callers provide one?
-    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, dsid, error);
+    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, dsid, nil, error);
 }
 
 bool SOSCCSetUserCredentialsAndDSIDWithAnalytics_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFDataRef parentEvent, CFErrorRef *error)
 {
-    return SOSCCAssertUserCredentialsAndOptionalDSIDWithAnalytics(user_label, user_password, dsid, (__bridge NSData*)parentEvent, error);
+    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, dsid, (__bridge NSData*)parentEvent, error);
 }
+
 bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
 {
-    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, NULL, error);
+    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, NULL, nil, error);
 }
 
 bool SOSCCCanAuthenticate_Server(CFErrorRef *error)
@@ -1108,33 +1095,24 @@ SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
     }) ? status : kSOSCCError;
 }
 
-bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
-{
-    __block bool result = true;
-    OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestToJoinCircle);
-
-    bool joined = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        result = SOSAccountJoinCircles(txn, block_error);
-        return result;
-    });
-
-    OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestToJoinCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestToJoinCircle), (int)joined);
-    return joined;
-}
-
 bool SOSCCRequestToJoinCircleWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error)
 {
     __block bool result = true;
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestToJoinCircle);
 
     bool requested = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        result = SOSAccountJoinCirclesWithAnalytics(txn, (__bridge NSData*)parentEvent, block_error);
+        result = SOSAccountJoinCircles(txn, (__bridge NSData*)parentEvent, block_error);
         return result;
     });
     OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestToJoinCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestToJoinCircle), (int)requested);
     return requested;
 }
 
+bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
+{
+    return SOSCCRequestToJoinCircleWithAnalytics_Server(nil, error);
+}
+
 bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error)
 {
     __block bool result = true;
@@ -1151,21 +1129,6 @@ bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error)
     return hasPublicKey;
 }
 
-bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
-{
-    __block bool result = true;
-    bool returned = false;
-    OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore);
-    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        SOSAccountEnsurePeerRegistration(txn.account, block_error);
-        result = SOSAccountJoinCirclesAfterRestore(txn, block_error);
-        return result;
-    });
-    OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore), (int)result);
-    return returned;
-
-}
-
 bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error)
 {
     __block bool result = true;
@@ -1173,19 +1136,28 @@ bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server(CFDataRef parentEv
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore);
     returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
         NSError* localError = nil;
-        SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:(__bridge NSData*)parentEvent error:&localError];
+        SFSignInAnalytics* parent = nil;
+        SFSignInAnalytics *ensurePeerRegistrationEvent = nil;
+        
+        if(parentEvent) {
+            parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:(__bridge NSData*)parentEvent error:&localError];
+            ensurePeerRegistrationEvent = [parent newSubTaskForEvent:@"ensurePeerRegistrationEvent"];
+        }
 
-        SFSignInAnalytics *ensurePeerRegistrationEvent = [parent newSubTaskForEvent:@"ensurePeerRegistrationEvent"];
         SOSAccountEnsurePeerRegistration(txn.account, block_error);
         if(block_error && *block_error){
             NSError* blockError = (__bridge NSError*)*block_error;
             if(blockError){
-                [ensurePeerRegistrationEvent logRecoverableError:blockError];
+                if(ensurePeerRegistrationEvent) {
+                    [ensurePeerRegistrationEvent logRecoverableError:blockError];
+                }
                 secerror("ensure peer registration error: %@", blockError);
             }
         }
-        [ensurePeerRegistrationEvent stopWithAttributes:nil];
-        result = SOSAccountJoinCirclesAfterRestoreWithAnalytics(txn, (__bridge NSData*)parentEvent, block_error);
+        if(ensurePeerRegistrationEvent) {
+            [ensurePeerRegistrationEvent stopWithAttributes:nil];
+        }
+        result = SOSAccountJoinCirclesAfterRestore(txn, (__bridge NSData*)parentEvent, block_error);
         return result;
     });
     OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore), (int)result);
@@ -1193,6 +1165,11 @@ bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server(CFDataRef parentEv
 
 }
 
+bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
+{
+    return SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server(nil, error);
+}
+
 bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
 {
        return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
@@ -1283,7 +1260,7 @@ bool SOSCCRemovePeersFromCircle_Server(CFArrayRef peers, CFErrorRef* error)
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRemovePeersFromCircle);
 
     bool removeResult =  do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        bool result = SOSAccountRemovePeersFromCircle(txn.account, peers, block_error);
+        bool result = SOSAccountRemovePeersFromCircle(txn.account, peers, nil, block_error);
         return result;
     });
     OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRemovePeersFromCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRemovePeersFromCircle), (int)removeResult);
@@ -1295,7 +1272,7 @@ bool SOSCCRemovePeersFromCircleWithAnalytics_Server(CFArrayRef peers, CFDataRef
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRemovePeersFromCircle);
 
     bool removeResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        bool result = SOSAccountRemovePeersFromCircleWithAnalytics(txn.account, peers, (__bridge NSData*)parentEvent, block_error);
+        bool result = SOSAccountRemovePeersFromCircle(txn.account, peers, (__bridge NSData*)parentEvent, block_error);
         return result;
     });
     OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRemovePeersFromCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRemovePeersFromCircle), (int)removeResult);