]> git.saurik.com Git - apple/security.git/blob - SecurityTool/sharedTool/policy_dryrun.m
Security-59754.41.1.tar.gz
[apple/security.git] / SecurityTool / sharedTool / policy_dryrun.m
1 #import <CloudKit/CKContainer_Private.h>
2 #import <CloudKit/CloudKit.h>
3 #import <CoreFoundation/CFPriv.h>
4 #import <Foundation/Foundation.h>
5 #import <Foundation/NSURLSession.h>
6 #import <dispatch/dispatch.h>
7 #import <os/variant_private.h>
8
9 #include "policy_dryrun.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <Security/SecItem.h>
16 #include <Security/SecItemPriv.h>
17 #include <Security/Security.h>
18 #include <keychain/SecureObjectSync/SOSCloudCircle.h>
19 #include <keychain/SecureObjectSync/SOSViews.h>
20 #import "TrustedPeers/TrustedPeers.h"
21
22 #include "tool_errors.h"
23
24 // Depends on a modern enough TrustedPeers framework.
25 // If the machine doesn't have one installed, set DYLD_FRAMEWORK_PATH
26
27 // Stolen from keychain/SecureObjectSync/SOSEngine.c
28
29 static NSString* getSOSView(id object, NSString* itemClass) {
30 if (![object isKindOfClass:[NSDictionary class]]) {
31 return nil;
32 }
33
34 NSString *viewHint = object[(NSString*)kSecAttrSyncViewHint];
35 if (viewHint != nil) {
36 return viewHint;
37 } else {
38 NSString *ag = object[(NSString*)kSecAttrAccessGroup];
39
40 if ([itemClass isEqualToString: (NSString*)kSecClassKey] && [ag isEqualToString: @"com.apple.security.sos"]) {
41 return (NSString*)kSOSViewiCloudIdentity;
42 } else if ([ag isEqualToString: @"com.apple.cfnetwork"]) {
43 return (NSString*)kSOSViewAutofillPasswords;
44 } else if ([ag isEqualToString: @"com.apple.safari.credit-cards"]) {
45 return (NSString*)kSOSViewSafariCreditCards;
46 } else if ([itemClass isEqualToString: (NSString*)kSecClassGenericPassword]) {
47 if ([ag isEqualToString: @"apple"] &&
48 [object[(NSString*)kSecAttrService] isEqualToString: @"AirPort"]) {
49 return (NSString*)kSOSViewWiFi;
50 } else if ([ag isEqualToString: @"com.apple.sbd"]) {
51 return (NSString*)kSOSViewBackupBagV0;
52 } else {
53 return (NSString*)kSOSViewOtherSyncable; // (genp)
54 }
55 } else {
56 return (NSString*)kSOSViewOtherSyncable; // (inet || keys)
57 }
58 }
59 }
60
61 static inline NSNumber *now_msecs() {
62 return @(((long)[[NSDate date] timeIntervalSince1970] * 1000));
63 }
64
65 static NSString* cloudKitDeviceID() {
66 __block NSString* ret = nil;
67
68 if (!os_variant_has_internal_diagnostics("com.apple.security")) {
69 return nil;
70 }
71 CKContainer *container = [CKContainer containerWithIdentifier:@"com.apple.security.keychain"];
72 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
73 [container fetchCurrentDeviceIDWithCompletionHandler:^(NSString* deviceID, NSError* error) {
74 if (error != nil) {
75 NSLog(@"failed to fetch CK deviceID: %@", error);
76 } else {
77 ret = deviceID;
78 }
79 dispatch_semaphore_signal(sem);
80 }];
81 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
82 return ret;
83 }
84
85 static void reportStats(unsigned expected_mismatches, unsigned real_mismatches, NSArray<NSDictionary*>* reportedMismatches) {
86 NSError *error = nil;
87 NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
88 NSURLSession *session = [NSURLSession sessionWithConfiguration:defaultConfiguration];
89 NSURL *endpoint = [NSURL URLWithString:@"https://xp.apple.com/report/2/xp_sear_keysync"];
90 NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
91 req.URL = endpoint;
92 req.HTTPMethod = @"POST";
93 NSMutableDictionary *dict = [@{
94 @"expectedMismatches": @(expected_mismatches),
95 @"realMismatches": @(real_mismatches),
96 @"eventTime": now_msecs(),
97 @"topic": @"xp_sear_keysync",
98 @"eventType": @"policy-dryrun",
99 } mutableCopy];
100 NSDictionary *version = CFBridgingRelease(_CFCopySystemVersionDictionary());
101 NSString *build = version[(__bridge NSString *)_kCFSystemVersionBuildVersionKey];
102 if (build != nil) {
103 dict[@"build"] = build;
104 } else {
105 NSLog(@"Unable to find out build version");
106 }
107 NSString *product = version[(__bridge NSString *)_kCFSystemVersionProductNameKey];
108 if (product != nil) {
109 dict[@"product"] = product;
110 } else {
111 NSLog(@"Unable to find out build product");
112 }
113 NSString* ckDeviceID = cloudKitDeviceID();
114 if (ckDeviceID) {
115 dict[@"SFAnalyticsDeviceID"] = ckDeviceID;
116 } else {
117 NSLog(@"Unable to fetch CK device ID");
118 }
119
120 dict[@"mismatches"] = reportedMismatches;
121
122 NSArray<NSDictionary*>* events = @[dict];
123 NSDictionary *wrapper = @{
124 @"postTime": @([[NSDate date] timeIntervalSince1970] * 1000),
125 @"events": events,
126 };
127 NSData* data = [NSJSONSerialization dataWithJSONObject:wrapper options:0 error:&error];
128 if (data == nil || error != nil) {
129 NSLog(@"failed to encode data: %@", error);
130 return;
131 }
132 NSLog(@"logging %@, %@", wrapper, data);
133 req.HTTPBody = data;
134 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
135 NSURLSessionDataTask *task = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
136 if (error != nil) {
137 NSLog(@"splunk upload failed: %@", error);
138 return;
139 }
140 NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*)response;
141 if (httpResp.statusCode != 200) {
142 NSLog(@"HTTP Post error: %@", httpResp);
143 }
144 NSLog(@"%@", httpResp);
145 if (data != nil) {
146 size_t length = [data length];
147 char *buf = malloc(length);
148 [data getBytes:buf length:length];
149 NSLog(@"%.*s", (int)length, buf);
150 free(buf);
151 }
152 dispatch_semaphore_signal(sem);
153 }];
154 [task resume];
155 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
156 }
157
158 int policy_dryrun(int argc, char * const *argv) {
159 @autoreleasepool {
160 NSError* error = nil;
161 // From Swift.policy, policy v7
162
163 TPPolicyDocument *tpd = [TPPolicyDocument policyDocWithHash:@"SHA256:dL8Qujqzprhp6FdH5GzNMtPlnZtLWMwfiiF7aykr8WU="
164 data:[[NSData alloc] initWithBase64EncodedString:
165 @"CAcSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSDAoEaVBvZBIEZnVsbBILCgNNYWMSBGZ1bGwSDAoEaU1hYxIEZnVsbBINCgdBcHBsZVRWEgJ0dhIOCgVXYXRjaBIFd2F0Y2gSFwoOQXVkaW9BY2Nlc3NvcnkSBWF1ZGlvGh4KBEhvbWUSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoYCglQYXNzd29yZHMSBGZ1bGwSBXdhdGNoGh8KEFNlY3VyZU9iamVjdFN5bmMSBGZ1bGwSBXdhdGNoGh4KBFdpRmkSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aGgoLQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhcKCEFwcGxlUGF5EgRmdWxsEgV3YXRjaBoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoGhkKCkF1dG9VbmxvY2sSBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaFQoGRW5ncmFtEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaCITCgRmdWxsEgRmdWxsEgV3YXRjaCIVCgJ0dhIEZnVsbBIFd2F0Y2gSAnR2IhQKBXdhdGNoEgRmdWxsEgV3YXRjaCIbCgVhdWRpbxIEZnVsbBIFd2F0Y2gSBWF1ZGlvMiIKFgAEIhICBHZ3aHQKCl5BcHBsZVBheSQSCEFwcGxlUGF5MiYKGAAEIhQCBHZ3aHQKDF5BdXRvVW5sb2NrJBIKQXV0b1VubG9jazIeChQABCIQAgR2d2h0CgheRW5ncmFtJBIGRW5ncmFtMh4KFAAEIhACBHZ3aHQKCF5IZWFsdGgkEgZIZWFsdGgyGgoSAAQiDgIEdndodAoGXkhvbWUkEgRIb21lMiAKFQAEIhECBHZ3aHQKCV5NYW5hdGVlJBIHTWFuYXRlZTI4CiEABCIdAgR2d2h0ChVeTGltaXRlZFBlZXJzQWxsb3dlZCQSE0xpbWl0ZWRQZWVyc0FsbG93ZWQyXQpQAAISHgAEIhoCBHZ3aHQKEl5Db250aW51aXR5VW5sb2NrJBIVAAQiEQIEdndodAoJXkhvbWVLaXQkEhUABCIRAgR2d2h0CgleQXBwbGVUViQSCU5vdFN5bmNlZDIrChsABCIXAgRhZ3JwCg9eWzAtOUEtWl17MTB9XC4SDEFwcGxpY2F0aW9uczLKAQq1AQACEjYAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAodAAQiGQIEYWdycAoRXmNvbVwuYXBwbGVcLnNiZCQSQAABChMABCIPAgVjbGFzcwoGXmtleXMkCicABCIjAgRhZ3JwChteY29tXC5hcHBsZVwuc2VjdXJpdHlcLnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTKdAwqDAwACEhgABCIUAgR2d2h0CgxeUENTLUJhY2t1cCQSGgAEIhYCBHZ3aHQKDl5QQ1MtQ2xvdWRLaXQkEhgABCIUAgR2d2h0CgxeUENTLUVzY3JvdyQSFQAEIhECBHZ3aHQKCV5QQ1MtRkRFJBIaAAQiFgIEdndodAoOXlBDUy1GZWxkc3BhciQSGgAEIhYCBHZ3aHQKDl5QQ1MtTWFpbERyb3AkEhoABCIWAgR2d2h0Cg5eUENTLU1haWxkcm9wJBIbAAQiFwIEdndodAoPXlBDUy1NYXN0ZXJLZXkkEhcABCITAgR2d2h0CgteUENTLU5vdGVzJBIYAAQiFAIEdndodAoMXlBDUy1QaG90b3MkEhkABCIVAgR2d2h0Cg1eUENTLVNoYXJpbmckEh4ABCIaAgR2d2h0ChJeUENTLWlDbG91ZEJhY2t1cCQSHQAEIhkCBHZ3aHQKEV5QQ1MtaUNsb3VkRHJpdmUkEhoABCIWAgR2d2h0Cg5eUENTLWlNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMj0KLgAEIioCBGFncnAKIl5jb21cLmFwcGxlXC5zYWZhcmlcLmNyZWRpdC1jYXJkcyQSC0NyZWRpdENhcmRzMjAKIwAEIh8CBGFncnAKF15jb21cLmFwcGxlXC5jZm5ldHdvcmskEglQYXNzd29yZHMybQpcAAISHgAEIhoCBHZ3aHQKEl5BY2Nlc3NvcnlQYWlyaW5nJBIaAAQiFgIEdndodAoOXk5hbm9SZWdpc3RyeSQSHAAEIhgCBHZ3aHQKEF5XYXRjaE1pZ3JhdGlvbiQSDURldmljZVBhaXJpbmcyDgoCAAYSCEJhY2tzdG9w" options:0]];
166 TPPolicy *policy = [tpd policyWithSecrets:@{} decrypter:nil error:&error];
167 if (error != nil) {
168 NSLog(@"policy error: %@", error);
169 return 1;
170 }
171 if (policy == nil) {
172 NSLog(@"policy is nil");
173 return 1;
174 }
175
176 TPSyncingPolicy* syncingPolicy = [policy syncingPolicyForModel:@"iPhone"
177 syncUserControllableViews:TPPBPeerStableInfo_UserControllableViewStatus_UNKNOWN
178 error:&error];
179 if(syncingPolicy == nil || error != nil) {
180 NSLog(@"syncing policy is nil: %@", error);
181 return 1;
182 }
183
184 unsigned real_mismatches = 0;
185 unsigned expected_mismatches = 0;
186 NSMutableArray<NSDictionary*>* reportedMismatches = [[NSMutableArray<NSDictionary*> alloc] init];
187
188 NSArray<NSString*>* keychainClasses = @[(id)kSecClassInternetPassword,
189 (id)kSecClassGenericPassword,
190 (id)kSecClassKey,
191 (id)kSecClassCertificate];
192
193 for(NSString* itemClass in keychainClasses) {
194 NSLog(@"itemClass: %@", itemClass);
195 NSDictionary *query = @{ (id)kSecMatchLimit : (id)kSecMatchLimitAll,
196 (id)kSecClass : (id)itemClass,
197 (id)kSecReturnAttributes : @YES,
198 (id)kSecAttrSynchronizable: @YES,
199 (id)kSecUseDataProtectionKeychain: @YES,
200 (id)kSecUseAuthenticationUI : (id)kSecUseAuthenticationUISkip,
201 };
202
203 NSArray *result;
204
205 OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (void*)&result);
206 if (status) {
207 if (status == errSecItemNotFound) {
208 NSLog(@"no items found matching: %@", query);
209 continue;
210 } else {
211 NSLog(@"SecItemCopyMatching(%@) failed: %d", query, (int)status);
212 return status;
213 }
214 }
215
216 if (![result isKindOfClass:[NSArray class]]) {
217 NSLog(@"expected NSArray result from SecItemCopyMatching");
218 return -1;
219 }
220
221 for (id a in result) {
222 NSLog(@"%@", a);
223 NSString *oldView = getSOSView(a, itemClass);
224 if (oldView != nil) {
225 NSLog(@"old: %@", oldView);
226 }
227
228 NSMutableDictionary* mutA = [a mutableCopy];
229 mutA[(id)kSecClass] = (id)itemClass;
230
231 NSString* newView = [syncingPolicy mapDictionaryToView:mutA];
232 if (newView != nil) {
233 NSLog(@"new: %@", newView);
234 }
235 if(oldView == nil ^ newView == nil) {
236 NSLog(@"real mismatch: old view (%@) != new view (%@)", oldView, newView);
237 ++real_mismatches;
238 [reportedMismatches addObject: a];
239
240 } else if (oldView && newView && ![oldView isEqualToString: newView]) {
241 if ([oldView hasPrefix:@"PCS-"] && [newView isEqualToString: @"ProtectedCloudStorage"]) {
242 NSLog(@"(expected PCS mismatch): old view (%@) != new view (%@)", oldView, newView);
243 ++expected_mismatches;
244
245 } else if([oldView isEqualToString:@"OtherSyncable"] && [newView isEqualToString: @"Applications"]) {
246 NSLog(@"(expected 3rd party mismatch): old view (%@) != new view (%@)", oldView, newView);
247 ++expected_mismatches;
248
249 } else if([oldView isEqualToString:@"OtherSyncable"] && [newView isEqualToString: @"Backstop"]) {
250 NSLog(@"(expected backstop mismatch): old view (%@) != new view (%@)", oldView, newView);
251 ++expected_mismatches;
252
253 } else if([newView isEqualToString:@"NotSynced"]) {
254 NSLog(@"(expected NotSynced mismatch): old view (%@) != new view (%@)", oldView, newView);
255 ++expected_mismatches;
256
257 } else if(([oldView isEqualToString:@"BackupBagV0"] || [oldView isEqualToString:@"iCloudIdentity"]) && [newView isEqualToString:@"SecureObjectSync"]) {
258 NSLog(@"(expected BackupBag - SecureObjectSync mismatch): old view (%@) != new view (%@)", oldView, newView);
259 ++expected_mismatches;
260
261 } else if(([oldView isEqualToString:@"AccessoryPairing"]
262 || [oldView isEqualToString:@"NanoRegistry"]
263 || [oldView isEqualToString:@"WatchMigration"]) && [newView isEqualToString:@"DevicePairing"]) {
264 NSLog(@"(expected DevicePairing mismatch): old view (%@) != new view (%@)", oldView, newView);
265 ++expected_mismatches;
266
267 } else {
268 NSLog(@"real mismatch: old view (%@) != new view (%@)", oldView, newView);
269 ++real_mismatches;
270 [reportedMismatches addObject: a];
271 }
272 }
273 }
274 }
275
276 NSLog(@"%d expected_mismatches", expected_mismatches);
277 NSLog(@"%d real_mismatches", real_mismatches);
278 reportStats(expected_mismatches, real_mismatches, reportedMismatches);
279 }
280 return 0;
281 }