]> git.saurik.com Git - apple/security.git/blob - secdxctests/KeychainAppClipTests.m
Security-59754.41.1.tar.gz
[apple/security.git] / secdxctests / KeychainAppClipTests.m
1 /*
2 * Copyright (c) 2020 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 #import <Security/Security.h>
25 #import <Security/SecItemPriv.h>
26 #include <Security/SecEntitlements.h>
27 #include <ipc/server_security_helpers.h>
28
29 #import "KeychainXCTest.h"
30
31 #if USE_KEYSTORE
32 @interface KeychainAppClipTests : KeychainXCTest
33 @end
34
35 @implementation KeychainAppClipTests {
36 // App Clips are only permitted to store items with agrp == appID, so we set and track it
37 NSString* _applicationIdentifier;
38 }
39
40 + (void)setUp {
41 [super setUp];
42 }
43
44 - (void)setUp {
45 [super setUp];
46 SecSecurityClientAppClipToRegular();
47 _applicationIdentifier = @"com.apple.security.appcliptests";
48 SecSecurityClientSetApplicationIdentifier((__bridge CFStringRef)_applicationIdentifier);
49 }
50
51 + (void)tearDown {
52 SecSecurityClientAppClipToRegular();
53 SecSecurityClientSetApplicationIdentifier(NULL);
54 [super tearDown];
55 }
56
57 # pragma mark - Test App Clip API Restrictions (SecItemAdd)
58
59 - (void)testAppclipCanAddItem {
60 SecSecurityClientRegularToAppClip();
61 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
62 NSDictionary* query = @{
63 (id)kSecClass : (id)kSecClassGenericPassword,
64 (id)kSecAttrService : @"AppClipTestService",
65 (id)kSecUseDataProtectionKeychain : @YES,
66 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
67 };
68 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
69 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
70 }
71
72 - (void)testAppClipAddNoSyncAllowed {
73 SecSecurityClientRegularToAppClip();
74 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
75 NSDictionary* query = @{
76 (id)kSecClass : (id)kSecClassGenericPassword,
77 (id)kSecAttrService : @"AppClipTestService",
78 (id)kSecUseDataProtectionKeychain : @YES,
79 (id)kSecAttrSynchronizable : @YES,
80 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
81 };
82 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecRestrictedAPI);
83 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
84 }
85
86 - (void)testAppClipAddNoAgrpAllowed {
87 SecSecurityClientRegularToAppClip();
88 // By not explicitly setting entitlements we get the default set which is not permitted for an app clip
89 NSDictionary* query = @{
90 (id)kSecClass : (id)kSecClassGenericPassword,
91 (id)kSecUseDataProtectionKeychain : @YES,
92 (id)kSecAttrService : @"AppClipTestService",
93 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
94 };
95 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecRestrictedAPI);
96
97 SecSecurityClientAppClipToRegular();
98 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
99 }
100
101 # pragma mark - Test App Clip API Restrictions (SecItemUpdate)
102
103 - (void)testAppClipCanUpdateItem {
104 SecSecurityClientRegularToAppClip();
105 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
106 NSMutableDictionary* query = [@{
107 (id)kSecClass : (id)kSecClassGenericPassword,
108 (id)kSecAttrService : @"AppClipTestService",
109 (id)kSecUseDataProtectionKeychain : @YES,
110 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
111 } mutableCopy];
112 SecItemAdd((__bridge CFDictionaryRef)query, NULL);
113
114 NSDictionary* update = @{
115 (id)kSecValueData : [@"different" dataUsingEncoding:NSUTF8StringEncoding],
116 (id)kSecAttrService : @"DifferentAppClipTestService",
117 };
118
119 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecSuccess);
120 query[(id)kSecAttrService] = @"DifferentAppClipTestService";
121 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
122 }
123
124 - (void)testAppClipUpdateNoSyncAllowed {
125 SecSecurityClientRegularToAppClip();
126 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
127 NSMutableDictionary* query = [@{
128 (id)kSecClass : (id)kSecClassGenericPassword,
129 (id)kSecAttrService : @"AppClipTestService",
130 (id)kSecUseDataProtectionKeychain : @YES,
131 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
132 } mutableCopy];
133 SecItemAdd((__bridge CFDictionaryRef)query, NULL);
134
135 NSDictionary* update = @{
136 (id)kSecValueData : [@"different" dataUsingEncoding:NSUTF8StringEncoding],
137 (id)kSecAttrSynchronizable : @YES,
138 };
139
140 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecRestrictedAPI);
141 query[(id)kSecAttrSynchronizable] = @YES;
142 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
143 }
144
145 - (void)testAppClipUpdateNoAgrpAllowed {
146 SecSecurityClientRegularToAppClip();
147 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
148 NSMutableDictionary* query = [@{
149 (id)kSecClass : (id)kSecClassGenericPassword,
150 (id)kSecAttrService : @"AppClipTestService",
151 (id)kSecUseDataProtectionKeychain : @YES,
152 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
153 } mutableCopy];
154 SecItemAdd((__bridge CFDictionaryRef)query, NULL);
155
156 NSDictionary* update = @{
157 (id)kSecValueData : [@"different" dataUsingEncoding:NSUTF8StringEncoding],
158 (id)kSecAttrAccessGroup : @"someotheraccessgroup",
159 };
160
161 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecMissingEntitlement);
162 query[(id)kSecAttrAccessGroup] = @"someotheraccessgroup";
163 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecMissingEntitlement);
164 }
165
166 # pragma mark - Test App Clip API Restrictions (SecItemCopyMatching)
167 // For now SICM doesn't care about sync, because there shouldn't be any items where (clip == 1 && sync == 1)
168
169 - (void)testAppClipCopyMatchingNoAgrpsAllowed {
170 SecSecurityClientRegularToAppClip();
171
172 NSDictionary* query = @{
173 (id)kSecClass : (id)kSecClassGenericPassword,
174 (id)kSecAttrService : @"AppClipTestService",
175 (id)kSecUseDataProtectionKeychain : @YES,
176 (id)kSecReturnAttributes : @YES,
177 };
178
179 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecRestrictedAPI);
180 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
181 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
182 }
183
184 # pragma mark - Test App Clip API Restrictions (SecItemDelete)
185
186 - (void)testAppClipDeleteNoAgrpsAllowed {
187 NSDictionary* query = @{
188 (id)kSecClass : (id)kSecClassGenericPassword,
189 (id)kSecAttrService : @"AppClipTestService",
190 (id)kSecUseDataProtectionKeychain : @YES,
191 };
192
193 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecItemNotFound);
194 SecSecurityClientRegularToAppClip();
195 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecRestrictedAPI);
196 }
197
198 # pragma mark - Test App Clip API Restrictions (Misc)
199
200 - (void)testAppClipNoSecItemUpdateTokenItemsAllowed {
201 SecSecurityClientRegularToAppClip();
202 // Don't bother setting it up, app clips aren't even welcome at the door.
203 XCTAssertEqual(SecItemUpdateTokenItemsForAccessGroups(NULL, (__bridge CFArrayRef)@[(id)kSecAttrAccessGroupToken], NULL), errSecRestrictedAPI);
204 }
205
206 - (void)testAppClipCanPassAppIDAccessGroup {
207 SecSecurityClientRegularToAppClip();
208 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
209
210 NSMutableDictionary* query = [@{
211 (id)kSecClass : (id)kSecClassGenericPassword,
212 (id)kSecAttrService : @"AppClipTestService",
213 (id)kSecUseDataProtectionKeychain : @YES,
214 (id)kSecReturnAttributes : @YES,
215 (id)kSecAttrAccessGroup : _applicationIdentifier,
216 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
217 } mutableCopy];
218
219 NSDictionary* update = @{
220 (id)kSecValueData : [@"betterpassword" dataUsingEncoding:NSUTF8StringEncoding],
221 };
222
223 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
224 query[(id)kSecValueData] = nil;
225 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
226 query[(id)kSecReturnAttributes] = nil;
227 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecSuccess);
228 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecSuccess);
229 }
230
231 # pragma mark - Test Item Deletion SPI
232
233 - (void)testDeletionSPINoEntitlement {
234 XCTAssertEqual(SecItemDeleteKeychainItemsForAppClip((__bridge CFStringRef)@"nonexistent"), errSecMissingEntitlement);
235 }
236
237 - (void)testDeletionSPINoItems {
238 SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateAppClipDeletion, kCFBooleanTrue);
239 XCTAssertEqual(SecItemDeleteKeychainItemsForAppClip((__bridge CFStringRef)@"nonexistent"), errSecSuccess);
240 }
241
242 - (void)testDeletionSPIDeleteAppClipItem {
243 // The SPI does not check if app clip, and the API does not check private entitlement
244 SecSecurityClientRegularToAppClip();
245 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
246 SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateAppClipDeletion, kCFBooleanTrue);
247
248 NSDictionary* query = @{
249 (id)kSecClass : (id)kSecClassGenericPassword,
250 (id)kSecUseDataProtectionKeychain : @YES,
251 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
252 (id)kSecReturnAttributes : @YES,
253 };
254
255 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
256 XCTAssertEqual(SecItemDeleteKeychainItemsForAppClip((__bridge CFStringRef)_applicationIdentifier), errSecSuccess);
257 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
258 }
259
260 - (void)testDeletionSPILeaveRegularItemsAlone {
261 SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateAppClipDeletion, kCFBooleanTrue);
262 NSString* agrp = @"com.apple.keychain.test.notanappclip";
263 [self setEntitlements:@{ @"keychain-access-groups": @[agrp] } validated:YES];
264
265 NSDictionary* query = @{
266 (id)kSecClass : (id)kSecClassGenericPassword,
267 (id)kSecUseDataProtectionKeychain : @YES,
268 (id)kSecAttrAccessGroup : agrp,
269 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding]
270 };
271
272 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
273 XCTAssertEqual(SecItemDeleteKeychainItemsForAppClip((__bridge CFStringRef)agrp), errSecSuccess);
274 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
275 }
276
277 - (void)testDeletionSPILeaveOtherAppClipItemsAlone {
278 SecSecurityClientRegularToAppClip();
279 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
280 NSString* otherAppID = @"not-the-same-application-identifier";
281 SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementPrivateAppClipDeletion, kCFBooleanTrue);
282
283 NSDictionary* query = @{
284 (id)kSecClass : (id)kSecClassGenericPassword,
285 (id)kSecUseDataProtectionKeychain : @YES,
286 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
287 (id)kSecReturnAttributes : @YES,
288 };
289 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
290
291 [self setEntitlements:@{@"com.apple.application-identifier" : otherAppID} validated:YES];
292 SecSecurityClientSetApplicationIdentifier((__bridge CFStringRef)otherAppID);
293 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
294
295 XCTAssertEqual(SecItemDeleteKeychainItemsForAppClip((__bridge CFStringRef)_applicationIdentifier), errSecSuccess);
296 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
297
298 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
299 SecSecurityClientSetApplicationIdentifier((__bridge CFStringRef)_applicationIdentifier);
300 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
301 }
302
303 @end
304 #endif