CKKSLockStateTracker.m [plain text]
/*
* Copyright (c) 2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#if OCTAGON
#include <notify.h>
#include <dispatch/dispatch.h>
#include <utilities/SecAKSWrappers.h>
#import "keychain/ckks/CKKS.h"
#import "keychain/ckks/CKKSGroupOperation.h"
#import "keychain/ckks/CKKSLockStateTracker.h"
@interface CKKSLockStateTracker ()
@property bool isLocked;
@property dispatch_queue_t queue;
@property NSOperationQueue* operationQueue;
@end
@implementation CKKSLockStateTracker
- (instancetype)init {
if((self = [super init])) {
_queue = dispatch_queue_create("lock-state-tracker", DISPATCH_QUEUE_SERIAL);
_operationQueue = [[NSOperationQueue alloc] init];
_isLocked = true;
[self resetUnlockDependency];
__weak __typeof(self) weakSelf = self;
// If this is a live server, register with notify
if(!SecCKKSTestsEnabled()) {
int token = 0;
notify_register_dispatch(kUserKeybagStateChangeNotification, &token, _queue, ^(int t) {
[weakSelf _onqueueRecheck];
});
}
dispatch_async(_queue, ^{
[weakSelf _onqueueRecheck];
});
}
return self;
}
-(NSString*)description {
return [NSString stringWithFormat: @"<CKKSLockStateTracker: }
-(void)resetUnlockDependency {
if(self.unlockDependency == nil || ![self.unlockDependency isPending]) {
self.unlockDependency = [NSBlockOperation blockOperationWithBlock: ^{
secinfo("ckks", "Keybag unlocked");
}];
self.unlockDependency.name = @"keybag-unlocked-dependency";
}
}
+(bool)queryAKSLocked {
CFErrorRef aksError = NULL;
bool locked = true;
if(!SecAKSGetIsLocked(&locked, &aksError)) {
secerror("ckks: error querying lock state: CFReleaseNull(aksError);
}
return locked;
}
-(void)_onqueueRecheck {
dispatch_assert_queue(self.queue);
bool wasLocked = self.isLocked;
self.isLocked = [CKKSLockStateTracker queryAKSLocked];
if(wasLocked != self.isLocked) {
if(self.isLocked) {
// We're locked now.
[self resetUnlockDependency];
} else {
[self.operationQueue addOperation: self.unlockDependency];
self.unlockDependency = nil;
}
}
}
-(void)recheck {
dispatch_sync(self.queue, ^{
[self _onqueueRecheck];
});
}
-(bool)isLockedError:(NSError *)error {
return ([error.domain isEqualToString:@"securityd"] || [error.domain isEqualToString:(__bridge NSString*)kSecErrorDomain])
&& error.code == errSecInteractionNotAllowed;
}
@end
#endif // OCTAGON