2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #import "CKKSKeychainView.h"
25 #import "CKKSIncomingQueueOperation.h"
26 #import "CKKSIncomingQueueEntry.h"
27 #import "CKKSItemEncrypter.h"
28 #import "CKKSOutgoingQueueEntry.h"
30 #import "CKKSManifest.h"
31 #import "CKKSAnalytics.h"
32 #import "CKKSPowerCollection.h"
33 #import "keychain/ckks/CKKSCurrentItemPointer.h"
35 #include <securityd/SecItemServer.h>
36 #include <securityd/SecItemDb.h>
37 #include <Security/SecItemPriv.h>
39 #include <utilities/SecADWrapper.h>
43 @interface CKKSIncomingQueueOperation ()
44 @property bool newOutgoingEntries;
45 @property bool pendingClassAEntries;
46 @property bool missingKey;
49 @implementation CKKSIncomingQueueOperation
51 - (instancetype)init {
54 - (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)ckks errorOnClassAFailure:(bool)errorOnClassAFailure {
55 if(self = [super init]) {
58 // Can't process unless we have a reasonable key hierarchy.
59 if(ckks.keyStateReadyDependency) {
60 [self addDependency: ckks.keyStateReadyDependency];
63 [self addNullableDependency: ckks.holdIncomingQueueOperation];
65 _errorOnClassAFailure = errorOnClassAFailure;
66 _pendingClassAEntries = false;
68 [self linearDependencies:ckks.incomingQueueOperations];
70 if ([CKKSManifest shouldSyncManifests]) {
71 __weak __typeof(self) weakSelf = self;
72 __weak CKKSKeychainView* weakCKKS = ckks;
73 CKKSResultOperation* updateManifestOperation = [CKKSResultOperation operationWithBlock:^{
74 __strong __typeof(self) strongSelf = weakSelf;
75 __strong CKKSKeychainView* strongCKKS = weakCKKS;
76 __block NSError* error = nil;
77 if (!strongCKKS || !strongSelf) {
78 ckkserror("ckksincoming", strongCKKS, "update manifest operation fired for released object");
82 [strongCKKS dispatchSyncWithAccountKeys:^bool{
83 strongCKKS.latestManifest = [CKKSManifest latestTrustedManifestForZone:strongCKKS.zoneName error:&error];
85 strongSelf.error = error;
86 ckkserror("ckksincoming", strongCKKS, "failed to get latest manifest: %@", error);
94 updateManifestOperation.name = @"update-manifest-operation";
96 [ckks scheduleOperation:updateManifestOperation];
97 [self addSuccessDependency:updateManifestOperation];
103 - (bool)processNewCurrentItemPointers:(NSArray<CKKSCurrentItemPointer*>*)queueEntries withManifest:(CKKSManifest*)manifest egoManifest:(CKKSEgoManifest*)egoManifest
105 CKKSKeychainView* ckks = self.ckks;
107 NSError* error = nil;
108 for(CKKSCurrentItemPointer* p in queueEntries) {
109 if ([CKKSManifest shouldSyncManifests]) {
110 if (![manifest validateCurrentItem:p withError:&error]) {
111 ckkserror("ckksincoming", ckks, "Unable to validate current item pointer (%@) against manifest (%@)", p, manifest);
112 if ([CKKSManifest shouldEnforceManifests]) {
118 p.state = SecCKKSProcessedStateLocal;
120 [p saveToDatabase:&error];
121 ckksnotice("ckkspointer", ckks, "Saving new current item pointer: %@", p);
123 ckkserror("ckksincoming", ckks, "Error saving new current item pointer: %@ %@", error, p);
126 // Schedule a view change notification
127 [ckks.notifyViewChangedScheduler trigger];
130 if(queueEntries.count > 0) {
131 // Schedule a view change notification
132 [ckks.notifyViewChangedScheduler trigger];
135 return (error == nil);
138 - (bool)processQueueEntries:(NSArray<CKKSIncomingQueueEntry*>*)queueEntries withManifest:(CKKSManifest*)manifest egoManifest:(CKKSEgoManifest*)egoManifest
140 CKKSKeychainView* ckks = self.ckks;
142 NSMutableArray* newOrChangedRecords = [[NSMutableArray alloc] init];
143 NSMutableArray* deletedRecordIDs = [[NSMutableArray alloc] init];
145 for(id entry in queueEntries) {
147 ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
151 NSError* error = nil;
153 CKKSIncomingQueueEntry* iqe = (CKKSIncomingQueueEntry*) entry;
154 ckksnotice("ckksincoming", ckks, "ready to process an incoming queue entry: %@ %@ %@", iqe, iqe.uuid, iqe.action);
156 // Note that we currently unencrypt the item before deleting it, instead of just deleting it
157 // This finds the class, which is necessary for the deletion process. We could just try to delete
158 // across all classes, though...
159 NSMutableDictionary* attributes = [[CKKSItemEncrypter decryptItemToDictionary: iqe.item error:&error] mutableCopy];
160 if(!attributes || error) {
161 if([ckks.lockStateTracker isLockedError:error]) {
162 NSError* localerror = nil;
163 ckkserror("ckksincoming", ckks, "Keychain is locked; can't decrypt IQE %@", iqe);
164 CKKSKey* key = [CKKSKey tryFromDatabase:iqe.item.parentKeyUUID zoneID:ckks.zoneID error:&localerror];
165 if(localerror || ([key.keyclass isEqualToString:SecCKKSKeyClassA] && self.errorOnClassAFailure)) {
169 // If this isn't an error, make sure it gets processed later.
170 if([key.keyclass isEqualToString:SecCKKSKeyClassA] && !self.errorOnClassAFailure) {
171 self.pendingClassAEntries = true;
174 } else if ([error.domain isEqualToString:@"securityd"] && error.code == errSecItemNotFound) {
175 ckkserror("ckksincoming", ckks, "Coudn't find key in keychain; will attempt to poke key hierarchy: %@", error)
176 self.missingKey = true;
179 ckkserror("ckksincoming", ckks, "Couldn't decrypt IQE %@ for some reason: %@", iqe, error);
182 self.errorItemsProcessed += 1;
186 // Add the UUID (which isn't stored encrypted)
187 [attributes setValue: iqe.item.uuid forKey: (__bridge NSString*) kSecAttrUUID];
189 // Add the PCS plaintext fields, if they exist
190 if(iqe.item.plaintextPCSServiceIdentifier) {
191 [attributes setValue: iqe.item.plaintextPCSServiceIdentifier forKey: (__bridge NSString*) kSecAttrPCSPlaintextServiceIdentifier];
193 if(iqe.item.plaintextPCSPublicKey) {
194 [attributes setValue: iqe.item.plaintextPCSPublicKey forKey: (__bridge NSString*) kSecAttrPCSPlaintextPublicKey];
196 if(iqe.item.plaintextPCSPublicIdentity) {
197 [attributes setValue: iqe.item.plaintextPCSPublicIdentity forKey: (__bridge NSString*) kSecAttrPCSPlaintextPublicIdentity];
200 // This item is also synchronizable (by definition)
201 [attributes setValue: @(YES) forKey: (__bridge NSString*) kSecAttrSynchronizable];
203 NSString* classStr = [attributes objectForKey: (__bridge NSString*) kSecClass];
204 if(![classStr isKindOfClass: [NSString class]]) {
205 self.error = [NSError errorWithDomain:@"securityd"
206 code:errSecInternalError
207 userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Item did not have a reasonable class: %@", classStr]}];
208 ckkserror("ckksincoming", ckks, "Synced item seems wrong: %@", self.error);
209 self.errorItemsProcessed += 1;
213 const SecDbClass * classP = !classStr ? NULL : kc_class_with_name((__bridge CFStringRef) classStr);
216 ckkserror("ckksincoming", ckks, "unknown class in object: %@ %@", classStr, iqe);
217 iqe.state = SecCKKSStateError;
218 [iqe saveToDatabase:&error];
220 ckkserror("ckksincoming", ckks, "Couldn't save errored IQE to database: %@", error);
223 self.errorItemsProcessed += 1;
227 if([iqe.action isEqualToString: SecCKKSActionAdd] || [iqe.action isEqualToString: SecCKKSActionModify]) {
228 BOOL requireManifestValidation = [CKKSManifest shouldEnforceManifests];
229 BOOL manifestValidatesItem = [manifest validateItem:iqe.item withError:&error];
231 if (!requireManifestValidation || manifestValidatesItem) {
232 [self _onqueueHandleIQEChange: iqe attributes:attributes class:classP];
233 [newOrChangedRecords addObject:[iqe.item CKRecordWithZoneID:ckks.zoneID]];
236 ckkserror("ckksincoming", ckks, "could not validate incoming item against manifest with error: %@", error);
237 if (![self _onqueueUpdateIQE:iqe withState:SecCKKSStateUnauthenticated error:&error]) {
238 ckkserror("ckksincoming", ckks, "failed to save incoming item back to database in unauthenticated state with error: %@", error);
241 self.errorItemsProcessed += 1;
244 } else if ([iqe.action isEqualToString: SecCKKSActionDelete]) {
245 BOOL requireManifestValidation = [CKKSManifest shouldEnforceManifests];
246 BOOL manifestValidatesDelete = ![manifest itemUUIDExistsInManifest:iqe.uuid];
248 if (!requireManifestValidation || manifestValidatesDelete) {
249 // if the item does not exist in the latest manifest, we're good to delete it
250 [self _onqueueHandleIQEDelete: iqe class:classP];
251 [deletedRecordIDs addObject:[[CKRecordID alloc] initWithRecordName:iqe.uuid zoneID:ckks.zoneID]];
254 // if the item DOES exist in the manifest, we can't trust the deletion
255 ckkserror("ckksincoming", ckks, "could not validate incoming item deletion against manifest");
256 if (![self _onqueueUpdateIQE:iqe withState:SecCKKSStateUnauthenticated error:&error]) {
257 ckkserror("ckksincoming", ckks, "failed to save incoming item deletion back to database in unauthenticated state with error: %@", error);
259 self.errorItemsProcessed += 1;
266 if(newOrChangedRecords.count > 0 || deletedRecordIDs > 0) {
267 // Schedule a view change notification
268 [ckks.notifyViewChangedScheduler trigger];
271 if(self.missingKey) {
272 [ckks.pokeKeyStateMachineScheduler trigger];
275 if ([CKKSManifest shouldSyncManifests]) {
276 [egoManifest updateWithNewOrChangedRecords:newOrChangedRecords deletedRecordIDs:deletedRecordIDs];
281 - (bool)_onqueueUpdateIQE:(CKKSIncomingQueueEntry*)iqe withState:(NSString*)newState error:(NSError**)error
283 if (![iqe.state isEqualToString:newState]) {
284 NSMutableDictionary* oldWhereClause = iqe.whereClauseToFindSelf.mutableCopy;
285 oldWhereClause[@"state"] = iqe.state;
286 iqe.state = newState;
287 if ([iqe saveToDatabase:error]) {
288 if (![CKKSSQLDatabaseObject deleteFromTable:[iqe.class sqlTable] where:oldWhereClause connection:NULL error:error]) {
301 // Synchronous, on some thread. Get back on the CKKS queue for thread-safety.
302 CKKSKeychainView* ckks = self.ckks;
304 ckkserror("ckksincoming", ckks, "no CKKS object");
308 __weak __typeof(self) weakSelf = self;
309 self.completionBlock = ^(void) {
310 __strong __typeof(self) strongSelf = weakSelf;
312 ckkserror("ckksincoming", ckks, "received callback for released object");
316 CKKSAnalytics* logger = [CKKSAnalytics logger];
318 if (!strongSelf.error) {
319 [logger logSuccessForEvent:CKKSEventProcessIncomingQueueClassC inView:ckks];
320 if (!strongSelf.pendingClassAEntries) {
321 [logger logSuccessForEvent:CKKSEventProcessIncomingQueueClassA inView:ckks];
324 [logger logRecoverableError:strongSelf.error
325 forEvent:strongSelf.errorOnClassAFailure ? CKKSEventProcessIncomingQueueClassA : CKKSEventProcessIncomingQueueClassC
327 withAttributes:NULL];
331 [ckks dispatchSync: ^bool{
333 ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
336 ckks.lastIncomingQueueOperation = self;
338 ckksnotice("ckksincoming", ckks, "Processing incoming queue");
340 if ([CKKSManifest shouldSyncManifests]) {
341 if (!ckks.latestManifest) {
342 // Until we can make manifests in our unit tests, we can't abort here
343 ckkserror("ckksincoming", ckks, "no manifest in ckks");
345 if (!ckks.egoManifest) {
346 ckkserror("ckksincoming", ckks, "no ego manifest in ckks");
350 bool ok = true; // Should commit transaction?
351 __block NSError* error = nil;
353 if ([CKKSManifest shouldSyncManifests]) {
354 NSInteger unauthenticatedItemCount = [CKKSIncomingQueueEntry countByState:SecCKKSStateUnauthenticated zone:ckks.zoneID error:&error];
355 if (error || unauthenticatedItemCount < 0) {
356 ckkserror("ckksincoming", ckks, "Error fetching incoming queue state counts: %@", error);
361 // take any existing unauthenticated entries and put them back in the new state
362 NSArray<CKKSIncomingQueueEntry*>* unauthenticatedEntries = nil;
363 NSString* lastMaxUUID = nil;
364 NSInteger numEntriesProcessed = 0;
365 while (numEntriesProcessed < unauthenticatedItemCount && (unauthenticatedEntries == nil || unauthenticatedEntries.count == SecCKKSIncomingQueueItemsAtOnce)) {
367 ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
371 unauthenticatedEntries = [CKKSIncomingQueueEntry fetch:SecCKKSIncomingQueueItemsAtOnce
372 startingAtUUID:lastMaxUUID
373 state:SecCKKSStateUnauthenticated
377 ckkserror("ckksincoming", ckks, "Error fetching unauthenticated queue records: %@", error);
382 if (unauthenticatedEntries.count == 0) {
383 ckksinfo("ckksincoming", ckks, "No unauthenticated entries in incoming queue to process");
387 for (CKKSIncomingQueueEntry* unauthenticatedEntry in unauthenticatedEntries) {
388 if (![self _onqueueUpdateIQE:unauthenticatedEntry withState:SecCKKSStateNew error:&error]) {
389 ckkserror("ckksincoming", ckks, "Error saving unauthenticated entry back to new state: %@", error);
394 lastMaxUUID = ([lastMaxUUID compare:unauthenticatedEntry.uuid] == NSOrderedDescending) ? lastMaxUUID : unauthenticatedEntry.uuid;
399 // Iterate through all incoming queue entries a chunk at a time (for peak memory concerns)
400 NSArray<CKKSIncomingQueueEntry*> * queueEntries = nil;
401 NSString* lastMaxUUID = nil;
402 while(queueEntries == nil || queueEntries.count == SecCKKSIncomingQueueItemsAtOnce) {
404 ckksnotice("ckksincoming", ckks, "CKKSIncomingQueueOperation cancelled, quitting");
408 queueEntries = [CKKSIncomingQueueEntry fetch: SecCKKSIncomingQueueItemsAtOnce
409 startingAtUUID:lastMaxUUID
410 state:SecCKKSStateNew
415 ckkserror("ckksincoming", ckks, "Error fetching incoming queue records: %@", error);
420 if([queueEntries count] == 0) {
421 // Nothing to do! exit.
422 ckksnotice("ckksincoming", ckks, "Nothing in incoming queue to process");
426 [CKKSPowerCollection CKKSPowerEvent:kCKKSPowerEventOutgoingQueue zone:ckks.zoneName count:[queueEntries count]];
428 if (![self processQueueEntries:queueEntries withManifest:ckks.latestManifest egoManifest:ckks.egoManifest]) {
429 ckksnotice("ckksincoming", ckks, "processQueueEntries didn't complete successfully");
433 // Find the highest UUID for the next fetch.
434 for(CKKSIncomingQueueEntry* iqe in queueEntries) {
435 lastMaxUUID = ([lastMaxUUID compare:iqe.uuid] == NSOrderedDescending) ? lastMaxUUID : iqe.uuid;
439 // Process other queues: CKKSCurrentItemPointers
440 ckksnotice("ckksincoming", ckks, "Processed %lu items in incoming queue (%lu errors)", self.successfulItemsProcessed, self.errorItemsProcessed);
442 NSArray<CKKSCurrentItemPointer*>* newCIPs = [CKKSCurrentItemPointer remoteItemPointers:ckks.zoneID error:&error];
443 if(error || !newCIPs) {
444 ckkserror("ckksincoming", ckks, "Could not load remote item pointers: %@", error);
446 if (![self processNewCurrentItemPointers:newCIPs withManifest:ckks.latestManifest egoManifest:ckks.egoManifest]) {
449 ckksnotice("ckksincoming", ckks, "Processed %lu items in CIP queue", newCIPs.count);
452 if(self.newOutgoingEntries) {
453 // No operation group
454 [ckks processOutgoingQueue:nil];
457 if(self.pendingClassAEntries) {
458 [self.ckks processIncomingQueueAfterNextUnlock];
465 - (void)_onqueueHandleIQEChange: (CKKSIncomingQueueEntry*) iqe attributes:(NSDictionary*)attributes class:(const SecDbClass *)classP {
466 CKKSKeychainView* ckks = self.ckks;
468 ckkserror("ckksincoming", ckks, "no CKKS object");
472 dispatch_assert_queue(ckks.queue);
475 __block CFErrorRef cferror = NULL;
476 __block NSError* error = NULL;
478 SecDbItemRef item = SecDbItemCreateWithAttributes(NULL, classP, (__bridge CFDictionaryRef) attributes, KEYBAG_DEVICE, &cferror);
480 __block NSDate* moddate = (__bridge NSDate*) CFDictionaryGetValue(item->attributes, kSecAttrModificationDate);
482 ok = kc_with_dbt(true, &cferror, ^(SecDbConnectionRef dbt){
483 bool replaceok = SecDbItemInsertOrReplace(item, dbt, &cferror, ^(SecDbItemRef olditem, SecDbItemRef *replace) {
484 // If the UUIDs do not match, then select the item with the 'lower' UUID, and tell CKKS to
485 // delete the item with the 'higher' UUID.
486 // Otherwise, the cloud wins.
488 SecADAddValueForScalarKey((__bridge CFStringRef) SecCKKSAggdPrimaryKeyConflict,1);
490 // Note that SecDbItemInsertOrReplace CFReleases any replace pointer it's given, so, be careful
492 if(!CFDictionaryContainsKey(olditem->attributes, kSecAttrUUID)) {
493 // No UUID -> no good.
494 ckksnotice("ckksincoming", ckks, "Replacing item (it doesn't have a UUID) for %@", iqe.uuid);
496 *replace = CFRetainSafe(item);
501 CFStringRef itemUUID = CFDictionaryGetValue(item->attributes, kSecAttrUUID);
502 CFStringRef olditemUUID = CFDictionaryGetValue(olditem->attributes, kSecAttrUUID);
504 CFComparisonResult compare = CFStringCompare(itemUUID, olditemUUID, 0);
505 CKKSOutgoingQueueEntry* oqe = nil;
507 case kCFCompareLessThan:
508 // item wins; delete olditem
509 ckksnotice("ckksincoming", ckks, "Primary key conflict; replacing %@ with CK item %@", olditem, item);
511 *replace = CFRetainSafe(item);
512 moddate = (__bridge NSDate*) CFDictionaryGetValue(item->attributes, kSecAttrModificationDate);
515 oqe = [CKKSOutgoingQueueEntry withItem:olditem action:SecCKKSActionDelete ckks:ckks error:&error];
516 [oqe saveToDatabase: &error];
517 self.newOutgoingEntries = true;
519 case kCFCompareGreaterThan:
520 // olditem wins; don't change olditem; delete item
521 ckksnotice("ckksincoming", ckks, "Primary key conflict; dropping CK item %@", item);
523 oqe = [CKKSOutgoingQueueEntry withItem:item action:SecCKKSActionDelete ckks:ckks error:&error];
524 [oqe saveToDatabase: &error];
525 self.newOutgoingEntries = true;
529 case kCFCompareEqualTo:
530 // remote item wins; this is the normal update case
531 ckksnotice("ckksincoming", ckks, "Primary key conflict; replacing %@ with CK item %@", olditem, item);
533 *replace = CFRetainSafe(item);
534 moddate = (__bridge NSDate*) CFDictionaryGetValue(item->attributes, kSecAttrModificationDate);
540 // SecDbItemInsertOrReplace returns an error even when it succeeds.
541 if(!replaceok && SecErrorIsSqliteDuplicateItemError(cferror)) {
542 CFReleaseNull(cferror);
551 ckkserror("ckksincoming", ckks, "couldn't process item from IncomingQueue: %@", cferror);
552 SecTranslateError(&error, cferror);
555 iqe.state = SecCKKSStateError;
556 [iqe saveToDatabase:&error];
558 ckkserror("ckksincoming", ckks, "Couldn't save errored IQE to database: %@", error);
565 ckkserror("ckksincoming", ckks, "Couldn't handle IQE, but why?: %@", error);
571 ckksinfo("ckksincoming", ckks, "Correctly processed an IQE; deleting");
572 [iqe deleteFromDatabase: &error];
575 ckkserror("ckksincoming", ckks, "couldn't delete CKKSIncomingQueueEntry: %@", error);
577 self.errorItemsProcessed += 1;
579 self.successfulItemsProcessed += 1;
583 // Log the number of seconds it took to propagate this change
584 uint64_t secondsDelay = (uint64_t) ([[NSDate date] timeIntervalSinceDate:moddate]);
585 SecADClientPushValueForDistributionKey((__bridge CFStringRef) SecCKKSAggdPropagationDelay, secondsDelay);
589 ckksnotice("ckksincoming", ckks, "IQE not correctly processed, but why? %@ %@", error, cferror);
592 iqe.state = SecCKKSStateError;
593 [iqe saveToDatabase:&error];
595 ckkserror("ckksincoming", ckks, "Couldn't save errored IQE to database: %@", error);
599 self.errorItemsProcessed += 1;
603 - (void)_onqueueHandleIQEDelete: (CKKSIncomingQueueEntry*) iqe class:(const SecDbClass *)classP {
604 CKKSKeychainView* ckks = self.ckks;
606 ckkserror("ckksincoming", ckks, "no CKKS object");
610 dispatch_assert_queue(ckks.queue);
613 __block CFErrorRef cferror = NULL;
614 NSError* error = NULL;
615 NSDictionary* queryAttributes = @{(__bridge NSString*) kSecClass: (__bridge NSString*) classP->name,
616 (__bridge NSString*) kSecAttrUUID: iqe.uuid,
617 (__bridge NSString*) kSecAttrSyncViewHint: ckks.zoneID.zoneName,
618 (__bridge NSString*) kSecAttrSynchronizable: @(YES)};
619 ckksnotice("ckksincoming", ckks, "trying to delete with query: %@", queryAttributes);
620 Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
624 ckkserror("ckksincoming", ckks, "couldn't create query: %@", cferror);
625 SecTranslateError(&error, cferror);
630 ok = kc_with_dbt(true, &cferror, ^(SecDbConnectionRef dbt) {
631 return s3dl_query_delete(dbt, q, NULL, &cferror);
635 if(CFErrorGetCode(cferror) == errSecItemNotFound) {
636 ckkserror("ckksincoming", ckks, "couldn't delete item (as it's already gone); this is okay: %@", cferror);
638 CFReleaseNull(cferror);
640 ckkserror("ckksincoming", ckks, "couldn't delete item: %@", cferror);
641 SecTranslateError(&error, cferror);
643 query_destroy(q, NULL);
649 ok = query_notify_and_destroy(q, ok, &cferror);
652 ckkserror("ckksincoming", ckks, "couldn't delete query: %@", cferror);
653 SecTranslateError(&error, cferror);
659 ckksnotice("ckksincoming", ckks, "Correctly processed an IQE; deleting");
660 [iqe deleteFromDatabase: &error];
663 ckkserror("ckksincoming", ckks, "couldn't delete CKKSIncomingQueueEntry: %@", error);
665 self.errorItemsProcessed += 1;
667 self.successfulItemsProcessed += 1;
670 ckkserror("ckksincoming", ckks, "IQE not correctly processed, but why? %@ %@", error, cferror);
672 self.errorItemsProcessed += 1;