]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSAPSReceiver.m
Security-58286.1.32.tar.gz
[apple/security.git] / keychain / ckks / CKKSAPSReceiver.m
1 /*
2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if OCTAGON
25
26 #import "keychain/ckks/CKKSAPSReceiver.h"
27 #import "keychain/ckks/CKKSCondition.h"
28 #import <CloudKit/CloudKit_Private.h>
29 #include <utilities/debugging.h>
30
31 @interface CKKSAPSReceiver()
32 // If we receive >0 notifications for a CKRecordZoneID that hasn't been registered yet, give them a fake update when they register
33 @property NSMutableSet<CKRecordZoneID*>* undeliveredUpdates;
34 @end
35
36 @implementation CKKSAPSReceiver
37
38 + (instancetype)receiverForEnvironment:(NSString *)environmentName
39 namedDelegatePort:(NSString*)namedDelegatePort
40 apsConnectionClass:(Class<CKKSAPSConnection>)apsConnectionClass {
41 static NSMutableDictionary<NSString*, CKKSAPSReceiver*>* environmentMap = nil;
42
43 if(environmentName == nil) {
44 secnotice("ckkspush", "No push environment; not bringing up APS.");
45 return nil;
46 }
47
48 @synchronized([self class]) {
49 if(environmentMap == nil) {
50 environmentMap = [[NSMutableDictionary alloc] init];
51 }
52
53 CKKSAPSReceiver* recv = [environmentMap valueForKey: environmentName];
54
55 if(recv == nil) {
56 recv = [[CKKSAPSReceiver alloc] initWithEnvironmentName: environmentName namedDelegatePort:namedDelegatePort apsConnectionClass: apsConnectionClass];
57 [environmentMap setValue: recv forKey: environmentName];
58 }
59
60 return recv;
61 }
62 }
63
64 + (dispatch_queue_t)apsDeliveryQueue {
65 static dispatch_queue_t aps_dispatch_queue;
66 static dispatch_once_t onceToken;
67 dispatch_once(&onceToken, ^{
68 aps_dispatch_queue = dispatch_queue_create("aps-callback-queue", DISPATCH_QUEUE_SERIAL);
69 });
70 return aps_dispatch_queue;
71 }
72
73 - (instancetype)initWithEnvironmentName:(NSString*)environmentName
74 namedDelegatePort:(NSString*)namedDelegatePort
75 apsConnectionClass:(Class<CKKSAPSConnection>)apsConnectionClass {
76 if(self = [super init]) {
77 _apsConnectionClass = apsConnectionClass;
78 _apsConnection = NULL;
79
80 _undeliveredUpdates = [[NSMutableSet alloc] init];
81
82 // APS might be slow. This doesn't need to happen immediately, so let it happen later.
83 __weak __typeof(self) weakSelf = self;
84 dispatch_async([CKKSAPSReceiver apsDeliveryQueue], ^{
85 __strong __typeof(self) strongSelf = weakSelf;
86 if(!strongSelf) {
87 return;
88 }
89 strongSelf.apsConnection = [[strongSelf.apsConnectionClass alloc] initWithEnvironmentName:environmentName namedDelegatePort:namedDelegatePort queue:[CKKSAPSReceiver apsDeliveryQueue]];
90 strongSelf.apsConnection.delegate = strongSelf;
91
92 // The following string should match: [[NSBundle mainBundle] bundleIdentifier]
93 NSString* topic = [kCKPushTopicPrefix stringByAppendingString:@"com.apple.securityd"];
94 [strongSelf.apsConnection setEnabledTopics:@[topic]];
95 });
96
97 _zoneMap = [NSMapTable strongToWeakObjectsMapTable];
98 }
99 return self;
100 }
101
102 - (CKKSCondition*)register:(id<CKKSZoneUpdateReceiver>)receiver forZoneID:(CKRecordZoneID *)zoneID {
103 CKKSCondition* finished = [[CKKSCondition alloc] init];
104
105 __weak __typeof(self) weakSelf = self;
106 dispatch_async([CKKSAPSReceiver apsDeliveryQueue], ^{
107 __strong __typeof(self) strongSelf = weakSelf;
108 if(!strongSelf) {
109 secerror("ckks: received registration for released CKKSAPSReceiver");
110 return;
111 }
112
113 [self.zoneMap setObject:receiver forKey: zoneID];
114 if([strongSelf.undeliveredUpdates containsObject:zoneID]) {
115 [strongSelf.undeliveredUpdates removeObject:zoneID];
116
117 // Now, send the receiver its fake notification!
118 secerror("ckks: sending fake push to newly-registered zone(%@): %@", zoneID, receiver);
119 [receiver notifyZoneChange:nil];
120 }
121
122 [finished fulfill];
123 });
124
125 return finished;
126 }
127
128 #pragma mark - APS Delegate callbacks
129
130 - (void)connection:(APSConnection *)connection didReceivePublicToken:(NSData *)publicToken {
131 // no-op.
132 secnotice("ckkspush", "CKKSAPSDelegate initiated: %@", connection);
133 }
134
135 - (void)connection:(APSConnection *)connection didReceiveToken:(NSData *)token forTopic:(NSString *)topic identifier:(NSString *)identifier {
136 secnotice("ckkspush", "Received per-topic push token \"%@\" for topic \"%@\" identifier \"%@\" on connection %@", token, topic, identifier, connection);
137 }
138
139 - (void)connection:(APSConnection *)connection didReceiveIncomingMessage:(APSIncomingMessage *)message {
140 secnotice("ckkspush", "CKKSAPSDelegate received a message: %@ on connection %@", message, connection);
141
142 CKNotification* notification = [CKNotification notificationFromRemoteNotificationDictionary:message.userInfo];
143
144 if(notification.notificationType == CKNotificationTypeRecordZone) {
145 CKRecordZoneNotification* rznotification = (CKRecordZoneNotification*) notification;
146
147 // Find receiever in map
148 id<CKKSZoneUpdateReceiver> recv = [self.zoneMap objectForKey:rznotification.recordZoneID];
149 if(recv) {
150 [recv notifyZoneChange:rznotification];
151 } else {
152 secerror("ckks: received push for unregistered zone: %@", rznotification);
153 if(rznotification.recordZoneID) {
154 [self.undeliveredUpdates addObject: rznotification.recordZoneID];
155 }
156 }
157 } else {
158 secerror("ckks: unexpected notification: %@", notification);
159 }
160 }
161
162 @end
163
164 #endif // OCTAGON