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
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 void addToSearchList(SecKeychainRef keychain
) {
93 CFArrayRef searchList
= NULL
;
94 SecKeychainCopySearchList(&searchList
);
95 CFMutableArrayRef mutableSearchList
= CFArrayCreateMutableCopy(NULL
, CFArrayGetCount(searchList
) + 1, searchList
);
96 CFArrayAppendValue(mutableSearchList
, keychain
);
97 SecKeychainSetSearchList(mutableSearchList
);
98 CFRelease(searchList
);
99 CFRelease(mutableSearchList
);
103 /* Checks to be sure there are N elements in this search, and returns the first
105 static SecKeychainItemRef
checkN(char* testName
, const CFDictionaryRef query
, uint32_t n
) {
106 CFArrayRef results
= NULL
;
108 ok_status(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), "%s: SecItemCopyMatching", testName
);
110 is(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), errSecItemNotFound
, "%s: SecItemCopyMatching (for no items)", testName
);
113 SecKeychainItemRef item
= NULL
;
115 is(CFArrayGetCount(results
), n
, "%s: Wrong number of results", testName
);
117 ok(item
= (SecKeychainItemRef
) CFArrayGetValueAtIndex(results
, 0), "%s: Couldn't get item", testName
);
119 pass("make test numbers match");
121 } else if((!results
) && n
== 0) {
122 pass("%s: no results found (and none expected)", testName
);
123 pass("make test numbers match");
125 fail("%s: no results found (and %d expected)", testName
, n
);
126 fflush(stdout
); CFShow(query
); fflush(stdout
);
127 pass("make test numbers match");
133 #define checkNTests 3
136 static void readPasswordContentsWithResult(SecKeychainItemRef item
, OSStatus expectedResult
, CFStringRef expectedContents
) {
138 fail("no item passed to readPasswordContentsWithResult");
139 fail("Match test numbers");
140 fail("Match test numbers");
144 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
146 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
147 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
149 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
150 CFArrayAppendValue((CFMutableArrayRef
)itemList
, item
);
151 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
153 CFTypeRef results
= NULL
;
154 if(expectedContents
) {
155 is(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), expectedResult
, "%s: readPasswordContents: SecItemCopyMatching", testName
);
156 CFReleaseNull(query
);
159 ok(CFGetTypeID(results
) == CFDataGetTypeID(), "%s: result is not a data", testName
);
161 CFDataRef data
= (CFDataRef
) results
;
162 CFStringRef str
= CFStringCreateWithBytes(NULL
, CFDataGetBytePtr(data
), CFDataGetLength(data
), kCFStringEncodingUTF8
, false);
163 eq_cf(str
, expectedContents
, "%s: contents do not match", testName
);
165 CFReleaseNull(results
);
167 fail("Didn't get any results");
168 fail("Match test numbers");
171 is(SecItemCopyMatching(query
, (CFTypeRef
*) &results
), expectedResult
, "%s: readPasswordContents: expecting error %d", testName
, (int) expectedResult
);
172 pass("Match test numbers");
173 pass("Match test numbers");
176 #define readPasswordContentsWithResultTests 3
178 static void readPasswordContents(SecKeychainItemRef item
, CFStringRef expectedContents
) {
179 return readPasswordContentsWithResult(item
, errSecSuccess
, expectedContents
);
181 #define readPasswordContentsTests readPasswordContentsWithResultTests
183 static void changePasswordContents(SecKeychainItemRef item
, CFStringRef newPassword
) {
185 fail("no item passed to changePasswordContents");
189 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
191 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
193 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
194 CFArrayAppendValue((CFMutableArrayRef
)itemList
, item
);
195 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
197 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
198 CFDictionarySetValue(attrs
, kSecValueData
, CFDataCreate(NULL
, (const UInt8
*) CFStringGetCStringPtr(newPassword
, kCFStringEncodingUTF8
), CFStringGetLength(newPassword
)));
200 ok_status(SecItemUpdate(query
, attrs
), "%s: SecItemUpdate", testName
);
202 #define changePasswordContentsTests 1
204 static void deleteItem(SecKeychainItemRef item
) {
205 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
207 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
208 CFArrayAppendValue((CFMutableArrayRef
)itemList
, item
);
209 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
211 ok_status(SecItemDelete(query
), "%s: SecItemDelete single item", testName
);
212 CFReleaseNull(query
);
214 #define deleteItemTests 1
216 static void deleteItems(CFArrayRef items
) {
218 fail("no items passed to deleteItems");
222 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
223 CFDictionarySetValue(query
, kSecUseItemList
, items
);
225 size_t count
= (size_t) CFArrayGetCount(items
);
227 ok_status(SecItemDelete(query
), "%s: SecItemDelete %ld items", testName
, count
);
229 is(SecItemDelete(query
), errSecItemNotFound
, "%s: SecItemDelete no items", testName
);
231 CFReleaseNull(query
);
233 #define deleteItemsTests 1
235 /* Checks in with securityd to see how many prompts were generated since the last call to this function, and tests against the number expected.
236 Returns the number generated since the last call. */
237 static uint32_t checkPrompts(uint32_t expectedSinceLastCall
, char* explanation
) {
238 uint32_t currentPrompts
= UINT_MAX
;
239 uint32_t newPrompts
= UINT_MAX
;
240 ok_status(SecKeychainGetUserPromptAttempts(¤tPrompts
), "%s: SecKeychainGetUserPromptAttempts", testName
);
242 newPrompts
= currentPrompts
- promptAttempts
;
244 is(newPrompts
, expectedSinceLastCall
, "%s: wrong number of prompts: %s", testName
, explanation
);
245 promptAttempts
= currentPrompts
;
249 #define checkPromptsTests 2
251 #pragma clang diagnostic pop
253 #endif /* kc_helpers_h */