]> git.saurik.com Git - apple/security.git/blob - secdxctests/KeychainEntitlementsTest.m
Security-59306.11.20.tar.gz
[apple/security.git] / secdxctests / KeychainEntitlementsTest.m
1 /*
2 * Copyright (c) 2018 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
27 #import "KeychainXCTest.h"
28
29 #if USE_KEYSTORE
30 @interface KeychainEntitlementsTest : KeychainXCTest
31 @end
32
33 @implementation KeychainEntitlementsTest
34
35 - (void)testNoEntitlements {
36 NSDictionary *params = @{ (id)kSecUseDataProtectionKeychain: @YES,
37 (id)kSecClass: (id)kSecClassGenericPassword, (id)kSecAttrLabel: @"label", };
38
39 // Application with no keychain-related entitlements at all, but CopyMatching must work in order to support
40 // backward compatibility with smart-card-enabled macos applications (com.apple.token AG is added automatically in this case).
41 [self setEntitlements:@{} validated:false];
42 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
43
44 // However, write access is declined for such application.
45 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
46 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecMissingEntitlement);
47 }
48
49 #if TARGET_OS_OSX
50 - (void)testInvalidEntitlementsAppID {
51 NSDictionary *params;
52 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
53 (id)kSecClass: (id)kSecClassGenericPassword,
54 (id)kSecAttrLabel: @"label", };
55
56 // Un-validated app-identifier entitlements must disallow any access to the keychain.
57 [self setEntitlements:@{ @"com.apple.application-identifier": @"com.apple.test-app-identifier" } validated:NO];
58 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
59 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
60 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecMissingEntitlement);
61
62 // However, keychain-access-groups entitlements should work even if not validated, AMFI will take care
63 // about cases when keychain-access-groups is not correctly used and we have to support cases when
64 // process contains application-groups entitlement but that entitlement is not present in provisioned profile, thus
65 // failing entitlement validation test.
66 [self setEntitlements:@{ @"keychain-access-groups": @[@"com.apple.test-app-identifier"] } validated:NO];
67 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
68 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecSuccess);
69 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecSuccess);
70 }
71 #endif // TARGET_OS_OSX
72
73 - (void)testValidEntitlementsAppID {
74 NSDictionary *params;
75 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
76 (id)kSecClass: (id)kSecClassGenericPassword,
77 (id)kSecAttrLabel: @"label",
78 (id)kSecAttrAccessGroup: @"com.apple.test-app-identifier", };
79 #if TARGET_OS_OSX
80 [self setEntitlements:@{ @"com.apple.application-identifier": @"com.apple.test-app-identifier" } validated:YES];
81 #else
82 [self setEntitlements:@{ @"application-identifier": @"com.apple.test-app-identifier" } validated:YES];
83 #endif
84 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
85 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecSuccess);
86 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecSuccess);
87 }
88
89 - (void)testEntitlementsAssociatedAppID {
90 NSMutableDictionary *params = [@{(id)kSecUseDataProtectionKeychain: @YES,
91 (id)kSecClass: (id)kSecClassGenericPassword,
92 (id)kSecAttrLabel: @"label" } mutableCopy];
93
94 [self setEntitlements:@{
95 #if TARGET_OS_OSX
96 @"com.apple.application-identifier": @"com.apple.test-app-identifier",
97 #else
98 @"application-identifier": @"com.apple.test-app-identifier",
99 #endif
100 @"com.apple.developer.associated-application-identifier": @[ @"com.apple.test-associated-app-identifier" ]
101 } validated:YES];
102
103 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
104
105 // The associated app identifier is preferred over the 'regular' app identifier (in practice this is only relevant on macOS)
106 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecSuccess);
107 params[(id)kSecReturnAttributes] = @(YES);
108 CFTypeRef result = NULL;
109 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)params, &result), errSecSuccess);
110 XCTAssertTrue(CFEqual(CFDictionaryGetValue(result, kSecAttrAccessGroup), CFSTR("com.apple.test-associated-app-identifier")));
111 CFReleaseNull(result);
112 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecSuccess);
113 }
114
115 - (void)testDisallowTokenGroupWrite {
116 NSDictionary *params;
117
118 // Explicit com.apple.token agrp is not acceptable for writing operations, but acceptable for reading.
119 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
120 (id)kSecClass: (id)kSecClassGenericPassword,
121 (id)kSecAttrLabel: @"label",
122 (id)kSecAttrAccessGroup: (id)kSecAttrAccessGroupToken, };
123 [self setEntitlements:@{ @"com.apple.application-identifier": (id)kSecAttrAccessGroupToken } validated:YES];
124 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
125 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
126 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecMissingEntitlement);
127 }
128
129 #if TARGET_OS_OSX
130 - (void)testInvalidAppGroups {
131 NSDictionary *params;
132 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
133 (id)kSecClass: (id)kSecClassGenericPassword,
134 (id)kSecAttrLabel: @"label", };
135 [self setEntitlements:@{ @"com.apple.security.application-groups": @[@"com.apple.test-app-groups"] } validated:NO];
136
137 // Invalid access group entitlement should still allow querying com.apple.token
138 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
139
140 // But write-access is forbidden,
141 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
142 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecMissingEntitlement);
143
144 // Similarly as explicitly referring to AG specified in unverified entitlements.
145 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
146 (id)kSecClass: (id)kSecClassGenericPassword,
147 (id)kSecAttrLabel: @"label",
148 (id)kSecAttrAccessGroup: @"com.apple.test-app-groups", };
149 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
150 XCTAssertEqual(SecItemAdd((CFDictionaryRef)params, NULL), errSecMissingEntitlement);
151 XCTAssertEqual(SecItemDelete((CFDictionaryRef)params), errSecMissingEntitlement);
152
153 // Explicitly referring to com.apple.token should work fine too.
154 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
155 (id)kSecClass: (id)kSecClassGenericPassword,
156 (id)kSecAttrLabel: @"label",
157 (id)kSecAttrAccessGroup: (id)kSecAttrAccessGroupToken, };
158 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
159 }
160 #endif // TARGET_OS_OSX
161
162 - (void)testTokenItemsGroup {
163 NSDictionary *params;
164
165 // Add token items for testing into the keychain.
166 NSArray *tokenItems = @[ @{
167 (id)kSecClass: (id)kSecClassGenericPassword,
168 (id)kSecAttrAccessGroup: (id)kSecAttrAccessGroupToken,
169 (id)kSecAttrLabel: @"label",
170 } ];
171 XCTAssertEqual(SecItemUpdateTokenItems(@"com.apple.testtoken", (__bridge CFArrayRef)tokenItems), errSecSuccess);
172
173 [self setEntitlements:@{ @"com.apple.application-identifier": @"com.apple.test-app-identifier" } validated:YES];
174
175 // Query without explicit access group, should find item on macOS and not find it on iOS.
176 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
177 (id)kSecClass: (id)kSecClassGenericPassword,
178 (id)kSecAttrLabel: @"label", };
179 #if TARGET_OS_IPHONE
180 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecItemNotFound);
181 #else
182 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecSuccess);
183 #endif
184
185 // Query with explicit AG should work the same on both platforms.
186 params = @{ (id)kSecUseDataProtectionKeychain: @YES,
187 (id)kSecClass: (id)kSecClassGenericPassword,
188 (id)kSecAttrLabel: @"label",
189 (id)kSecAttrAccessGroup: (id)kSecAttrAccessGroupToken, };
190 XCTAssertEqual(SecItemCopyMatching((CFDictionaryRef)params, NULL), errSecSuccess);
191
192 // Delete all test token items.
193 SecItemUpdateTokenItems(@"com.apple.testtoken", (__bridge CFArrayRef)@[]);
194 }
195
196 - (void)testEntitlementForExplicitAccessGroupLacking {
197 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier", @"application-identifier": @"com.apple.test-app-identifier"} validated:YES];
198
199 NSMutableDictionary* query = [@{(id)kSecClass : (id)kSecClassGenericPassword,
200 (id)kSecUseDataProtectionKeychain : @YES,
201 (id)kSecAttrAccount : @"TestAccount",
202 (id)kSecAttrLabel : @"TestLabel",
203 (id)kSecAttrAccessGroup : @"com.apple.test.myaccessgroup",
204 (id)kSecValueData : [@"passwd" dataUsingEncoding:NSUTF8StringEncoding],
205 } mutableCopy];
206 NSMutableDictionary* update = [@{(id)kSecAttrLabel : @"NewLabel"} mutableCopy];
207
208 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecMissingEntitlement);
209 query[(id)kSecValueData] = nil;
210 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecMissingEntitlement);
211 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecMissingEntitlement);
212 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecMissingEntitlement);
213
214 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier", @"application-identifier": @"com.apple.test-app-identifier", @"keychain-access-groups": @[@"com.apple.test.myaccessgroup"]} validated:YES];
215 query[(id)kSecValueData] = [@"secret" dataUsingEncoding:NSUTF8StringEncoding];
216 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
217 query[(id)kSecValueData] = nil;
218
219 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
220 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier"} validated:YES];
221 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecMissingEntitlement);
222
223 query[(id)kSecAttrAccessGroup] = nil;
224 update[(id)kSecAttrAccessGroup] = @"com.apple.test.myotheraccessgroup";
225 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecMissingEntitlement);
226
227 query[(id)kSecAttrAccessGroup] = @"com.apple.test.myaccessgroup";
228 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier", @"application-identifier": @"com.apple.test-app-identifier", @"keychain-access-groups": @[@"com.apple.test.myaccessgroup"]} validated:YES];
229 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecSuccess, @"keychain item not deleted after querying without entitlements");
230 }
231
232 - (void)testEntitlementForImplicitAccessGroupLacking {
233 NSMutableDictionary* query = [@{(id)kSecClass : (id)kSecClassGenericPassword,
234 (id)kSecUseDataProtectionKeychain : @YES,
235 (id)kSecAttrAccount : @"TestAccount",
236 (id)kSecAttrLabel : @"TestLabel",
237 (id)kSecAttrAccessGroup : @"com.apple.test.myaccessgroup",
238 (id)kSecValueData : [@"passwd" dataUsingEncoding:NSUTF8StringEncoding],
239 } mutableCopy];
240 NSDictionary* update = @{(id)kSecAttrLabel : @"NewLabel"};
241
242 // Have to use explicit access group here or we just get the app identifier
243 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier", @"application-identifier": @"com.apple.test-app-identifier", @"keychain-access-groups": @[@"com.apple.test.myaccessgroup"]} validated:YES];
244 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
245
246 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier", @"application-identifier": @"com.apple.test-app-identifier"} validated:YES];
247 query[(id)kSecValueData] = nil;
248 query[(id)kSecAttrAccessGroup] = nil;
249
250 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecItemNotFound);
251 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecItemNotFound);
252 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecItemNotFound);
253
254 [self setEntitlements:@{@"com.apple.application-identifier": @"com.apple.test-app-identifier", @"application-identifier": @"com.apple.test-app-identifier", @"keychain-access-groups": @[@"com.apple.test.myaccessgroup"]} validated:YES];
255 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update), errSecSuccess);
256 query[(id)kSecAttrLabel] = @"NewLabel";
257
258 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
259 XCTAssertEqual(SecItemDelete((__bridge CFDictionaryRef)query), errSecSuccess, @"keychain item not deleted after querying without entitlements");
260 }
261
262 @end
263
264 #endif // USE_KEYSTORE