2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
30 #include <Security/Security.h>
31 #include <Security/SecKeychainPriv.h>
32 #include "utilities/SecCFRelease.h"
34 #include "kc-keychain-file-helpers.h"
36 /* redefine this since the headers are mixed up */
37 static inline bool CFEqualSafe(CFTypeRef left
, CFTypeRef right
)
39 if (left
== NULL
|| right
== NULL
)
42 return CFEqual(left
, right
);
45 static char keychainFile
[1000];
46 static char keychainDbFile
[1000];
47 static char keychainTempFile
[1000];
48 static char keychainName
[1000];
49 static char testName
[1000];
50 static uint32_t promptAttempts
;
52 #pragma clang diagnostic push
53 #pragma clang diagnostic ignored "-Wunused-variable"
54 #pragma clang diagnostic ignored "-Wunused-function"
56 static void startTest(const char* thisTestName
) {
57 strlcpy(testName
, thisTestName
, sizeof(testName
));
60 static void initializeKeychainTests(const char* thisTestName
) {
61 const char *home_dir
= getenv("HOME");
62 sprintf(keychainName
, "test-%s.asdf", thisTestName
);
63 sprintf(keychainFile
, "%s/Library/Keychains/%s", home_dir
, keychainName
);
64 sprintf(keychainDbFile
, "%s/Library/Keychains/%s-db", home_dir
, keychainName
);
65 sprintf(keychainTempFile
, "%s/Library/Keychains/test_temp", home_dir
);
67 deleteKeychainFiles(keychainFile
);
69 startTest(thisTestName
);
71 SecKeychainGetUserPromptAttempts(&promptAttempts
);
72 SecKeychainSetUserInteractionAllowed(FALSE
);
75 // Use this at the bottom of every test to make sure everything is gone
76 static void deleteTestFiles() {
77 deleteKeychainFiles(keychainFile
);
80 static SecKeychainRef CF_RETURNS_RETAINED
getPopulatedTestKeychain() {
81 deleteKeychainFiles(keychainFile
);
83 writeFile(keychainFile
, test_keychain
, sizeof(test_keychain
));
85 SecKeychainRef kc
= NULL
;
86 ok_status(SecKeychainOpen(keychainFile
, &kc
), "%s: getPopulatedTestKeychain: SecKeychainOpen", testName
);
87 ok_status(SecKeychainUnlock(kc
, (UInt32
) strlen(test_keychain_password
), test_keychain_password
, true), "%s: getPopulatedTestKeychain: SecKeychainUnlock", testName
);
90 #define getPopulatedTestKeychainTests 2
92 static SecKeychainRef CF_RETURNS_RETAINED
getEmptyTestKeychain() {
93 deleteKeychainFiles(keychainFile
);
95 SecKeychainRef kc
= NULL
;
96 ok_status(SecKeychainCreate(keychainFile
, (UInt32
) strlen(test_keychain_password
), test_keychain_password
, false, NULL
, &kc
), "%s: getPopulatedTestKeychain: SecKeychainCreate", testName
);
99 #define getEmptyTestKeychainTests 1
102 static void addToSearchList(SecKeychainRef keychain
) {
103 CFArrayRef searchList
= NULL
;
104 SecKeychainCopySearchList(&searchList
);
105 CFMutableArrayRef mutableSearchList
= CFArrayCreateMutableCopy(NULL
, CFArrayGetCount(searchList
) + 1, searchList
);
106 CFArrayAppendValue(mutableSearchList
, keychain
);
107 SecKeychainSetSearchList(mutableSearchList
);
108 CFRelease(searchList
);
109 CFRelease(mutableSearchList
);
113 /* Checks to be sure there are N elements in this search, and returns the first
115 static SecKeychainItemRef
checkNCopyFirst(char* testName
, const CFDictionaryRef CF_CONSUMED query
, uint32_t n
) {
116 CFArrayRef results
= NULL
;
118 ok_status(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), "%s: SecItemCopyMatching", testName
);
120 is(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), errSecItemNotFound
, "%s: SecItemCopyMatching (for no items)", testName
);
123 SecKeychainItemRef item
= NULL
;
125 is(CFArrayGetCount(results
), n
, "%s: Wrong number of results", testName
);
127 ok(item
= (SecKeychainItemRef
) CFArrayGetValueAtIndex(results
, 0), "%s: Couldn't get item", testName
);
129 pass("make test numbers match");
131 } else if((!results
) && n
== 0) {
132 pass("%s: no results found (and none expected)", testName
);
133 pass("make test numbers match");
135 fail("%s: no results found (and %d expected)", testName
, n
);
136 fflush(stdout
); CFShow(query
); fflush(stdout
);
137 pass("make test numbers match");
141 CFReleaseNull(results
);
147 static void checkN(char* testName
, const CFDictionaryRef CF_CONSUMED query
, uint32_t n
) {
148 SecKeychainItemRef item
= checkNCopyFirst(testName
, query
, n
);
152 #define checkNTests 3
155 static void readPasswordContentsWithResult(SecKeychainItemRef item
, OSStatus expectedResult
, CFStringRef expectedContents
) {
157 fail("no item passed to readPasswordContentsWithResult");
158 fail("Match test numbers");
159 fail("Match test numbers");
163 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
165 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
166 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
168 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
169 CFArrayAppendValue((CFMutableArrayRef
)itemList
, item
);
170 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
172 CFTypeRef results
= NULL
;
173 if(expectedContents
) {
174 is(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), expectedResult
, "%s: readPasswordContents: SecItemCopyMatching", testName
);
175 CFReleaseNull(query
);
178 ok(CFGetTypeID(results
) == CFDataGetTypeID(), "%s: result is not a data", testName
);
180 CFDataRef data
= (CFDataRef
) results
;
181 CFStringRef str
= CFStringCreateWithBytes(NULL
, CFDataGetBytePtr(data
), CFDataGetLength(data
), kCFStringEncodingUTF8
, false);
182 eq_cf(str
, expectedContents
, "%s: contents do not match", testName
);
184 CFReleaseNull(results
);
186 fail("Didn't get any results");
187 fail("Match test numbers");
190 is(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), expectedResult
, "%s: readPasswordContents: expecting error %d", testName
, (int) expectedResult
);
191 pass("Match test numbers");
192 pass("Match test numbers");
195 #define readPasswordContentsWithResultTests 3
197 static void readPasswordContents(SecKeychainItemRef item
, CFStringRef expectedContents
) {
198 return readPasswordContentsWithResult(item
, errSecSuccess
, expectedContents
);
200 #define readPasswordContentsTests readPasswordContentsWithResultTests
202 static void changePasswordContents(SecKeychainItemRef item
, CFStringRef newPassword
) {
204 fail("no item passed to changePasswordContents");
208 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
210 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
212 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
213 CFArrayAppendValue((CFMutableArrayRef
)itemList
, item
);
214 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
216 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
217 CFDataRef data
= CFDataCreate(NULL
, (const UInt8
*) CFStringGetCStringPtr(newPassword
, kCFStringEncodingUTF8
), CFStringGetLength(newPassword
));
218 CFDictionarySetValue(attrs
, kSecValueData
, data
);
221 ok_status(SecItemUpdate(query
, attrs
), "%s: SecItemUpdate", testName
);
223 #define changePasswordContentsTests 1
225 static void deleteItem(SecKeychainItemRef item
) {
226 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
228 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
229 CFArrayAppendValue((CFMutableArrayRef
)itemList
, item
);
230 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
232 ok_status(SecItemDelete(query
), "%s: SecItemDelete single item", testName
);
233 CFReleaseNull(query
);
235 #define deleteItemTests 1
237 static void deleteItems(CFArrayRef items
) {
239 fail("no items passed to deleteItems");
243 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
244 CFDictionarySetValue(query
, kSecUseItemList
, items
);
246 size_t count
= (size_t) CFArrayGetCount(items
);
248 ok_status(SecItemDelete(query
), "%s: SecItemDelete %ld items", testName
, count
);
250 is(SecItemDelete(query
), errSecItemNotFound
, "%s: SecItemDelete no items", testName
);
252 CFReleaseNull(query
);
254 #define deleteItemsTests 1
256 /* Checks in with securityd to see how many prompts were generated since the last call to this function, and tests against the number expected.
257 Returns the number generated since the last call. */
258 static uint32_t checkPrompts(uint32_t expectedSinceLastCall
, char* explanation
) {
259 uint32_t currentPrompts
= UINT_MAX
;
260 uint32_t newPrompts
= UINT_MAX
;
261 ok_status(SecKeychainGetUserPromptAttempts(¤tPrompts
), "%s: SecKeychainGetUserPromptAttempts", testName
);
263 newPrompts
= currentPrompts
- promptAttempts
;
265 is(newPrompts
, expectedSinceLastCall
, "%s: wrong number of prompts: %s", testName
, explanation
);
266 promptAttempts
= currentPrompts
;
270 #define checkPromptsTests 2
272 #pragma clang diagnostic pop
274 #endif /* kc_helpers_h */