--- /dev/null
+/*
+ * Copyright (c) 2016 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef kc_helpers_h
+#define kc_helpers_h
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <Security/Security.h>
+#include <Security/SecKeychainPriv.h>
+#include "utilities/SecCFRelease.h"
+
+#include "kc-keychain-file-helpers.h"
+
+/* redefine this since the headers are mixed up */
+static inline bool CFEqualSafe(CFTypeRef left, CFTypeRef right)
+{
+ if (left == NULL || right == NULL)
+ return left == right;
+ else
+ return CFEqual(left, right);
+}
+
+static char keychainFile[1000];
+static char keychainDbFile[1000];
+static char keychainTempFile[1000];
+static char keychainName[1000];
+static char testName[1000];
+static uint32_t promptAttempts;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic ignored "-Wunused-function"
+
+static void startTest(const char* thisTestName) {
+ strlcpy(testName, thisTestName, sizeof(testName));
+}
+
+static void initializeKeychainTests(const char* thisTestName) {
+ const char *home_dir = getenv("HOME");
+ sprintf(keychainName, "test-%s.asdf", thisTestName);
+ sprintf(keychainFile, "%s/Library/Keychains/%s", home_dir, keychainName);
+ sprintf(keychainDbFile, "%s/Library/Keychains/%s-db", home_dir, keychainName);
+ sprintf(keychainTempFile, "%s/Library/Keychains/test_temp", home_dir);
+
+ deleteKeychainFiles(keychainFile);
+
+ startTest(thisTestName);
+
+ SecKeychainGetUserPromptAttempts(&promptAttempts);
+ SecKeychainSetUserInteractionAllowed(FALSE);
+}
+
+// Use this at the bottom of every test to make sure everything is gone
+static void deleteTestFiles() {
+ deleteKeychainFiles(keychainFile);
+}
+
+static SecKeychainRef getPopulatedTestKeychain() {
+ deleteKeychainFiles(keychainFile);
+
+ writeFile(keychainFile, test_keychain, sizeof(test_keychain));
+
+ SecKeychainRef kc = NULL;
+ ok_status(SecKeychainOpen(keychainFile, &kc), "%s: getPopulatedTestKeychain: SecKeychainOpen", testName);
+ ok_status(SecKeychainUnlock(kc, (UInt32) strlen(test_keychain_password), test_keychain_password, true), "%s: getPopulatedTestKeychain: SecKeychainUnlock", testName);
+ return kc;
+}
+#define getPopulatedTestKeychainTests 2
+
+static void addToSearchList(SecKeychainRef keychain) {
+ CFArrayRef searchList = NULL;
+ SecKeychainCopySearchList(&searchList);
+ CFMutableArrayRef mutableSearchList = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(searchList) + 1, searchList);
+ CFArrayAppendValue(mutableSearchList, keychain);
+ SecKeychainSetSearchList(mutableSearchList);
+ CFRelease(searchList);
+ CFRelease(mutableSearchList);
+}
+
+
+/* Checks to be sure there are N elements in this search, and returns the first
+ * if it exists. */
+static SecKeychainItemRef checkN(char* testName, const CFDictionaryRef query, uint32_t n) {
+ CFArrayRef results = NULL;
+ if(n > 0) {
+ ok_status(SecItemCopyMatching(query, (CFTypeRef*) &results), "%s: SecItemCopyMatching", testName);
+ } else {
+ is(SecItemCopyMatching(query, (CFTypeRef*) &results), errSecItemNotFound, "%s: SecItemCopyMatching (for no items)", testName);
+ }
+
+ SecKeychainItemRef item = NULL;
+ if(results) {
+ is(CFArrayGetCount(results), n, "%s: Wrong number of results", testName);
+ if(n >= 1) {
+ ok(item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, 0), "%s: Couldn't get item", testName);
+ } else {
+ pass("make test numbers match");
+ }
+ } else if((!results) && n == 0) {
+ pass("%s: no results found (and none expected)", testName);
+ pass("make test numbers match");
+ } else {
+ fail("%s: no results found (and %d expected)", testName, n);
+ fflush(stdout); CFShow(query); fflush(stdout);
+ pass("make test numbers match");
+ }
+
+ CFRelease(query);
+ return item;
+}
+#define checkNTests 3
+
+
+static void readPasswordContentsWithResult(SecKeychainItemRef item, OSStatus expectedResult, CFStringRef expectedContents) {
+ if(!item) {
+ fail("no item passed to readPasswordContentsWithResult");
+ fail("Match test numbers");
+ fail("Match test numbers");
+ return;
+ }
+
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne);
+ CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
+
+ CFMutableArrayRef itemList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue((CFMutableArrayRef)itemList, item);
+ CFDictionarySetValue(query, kSecUseItemList, itemList);
+
+ CFTypeRef results = NULL;
+ if(expectedContents) {
+ is(SecItemCopyMatching(query, (CFTypeRef*) &results), expectedResult, "%s: readPasswordContents: SecItemCopyMatching", testName);
+ CFReleaseNull(query);
+
+ if(results) {
+ ok(CFGetTypeID(results) == CFDataGetTypeID(), "%s: result is not a data", testName);
+
+ CFDataRef data = (CFDataRef) results;
+ CFStringRef str = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(data), CFDataGetLength(data), kCFStringEncodingUTF8, false);
+ eq_cf(str, expectedContents, "%s: contents do not match", testName);
+ CFReleaseNull(str);
+ CFReleaseNull(results);
+ } else {
+ fail("Didn't get any results");
+ fail("Match test numbers");
+ }
+ } else {
+ is(SecItemCopyMatching(query, (CFTypeRef*) &results), expectedResult, "%s: readPasswordContents: expecting error %d", testName, (int) expectedResult);
+ pass("Match test numbers");
+ pass("Match test numbers");
+ }
+}
+#define readPasswordContentsWithResultTests 3
+
+static void readPasswordContents(SecKeychainItemRef item, CFStringRef expectedContents) {
+ return readPasswordContentsWithResult(item, errSecSuccess, expectedContents);
+}
+#define readPasswordContentsTests readPasswordContentsWithResultTests
+
+static void changePasswordContents(SecKeychainItemRef item, CFStringRef newPassword) {
+ if(!item) {
+ fail("no item passed to changePasswordContents");
+ return;
+ }
+
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne);
+
+ CFMutableArrayRef itemList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue((CFMutableArrayRef)itemList, item);
+ CFDictionarySetValue(query, kSecUseItemList, itemList);
+
+ CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(attrs, kSecValueData, CFDataCreate(NULL, (const UInt8*) CFStringGetCStringPtr(newPassword, kCFStringEncodingUTF8), CFStringGetLength(newPassword)));
+
+ ok_status(SecItemUpdate(query, attrs), "%s: SecItemUpdate", testName);
+}
+#define changePasswordContentsTests 1
+
+static void deleteItem(SecKeychainItemRef item) {
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFMutableArrayRef itemList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue((CFMutableArrayRef)itemList, item);
+ CFDictionarySetValue(query, kSecUseItemList, itemList);
+
+ ok_status(SecItemDelete(query), "%s: SecItemDelete single item", testName);
+ CFReleaseNull(query);
+}
+#define deleteItemTests 1
+
+static void deleteItems(CFArrayRef items) {
+ if(!items) {
+ fail("no items passed to deleteItems");
+ return;
+ }
+
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(query, kSecUseItemList, items);
+
+ size_t count = (size_t) CFArrayGetCount(items);
+ if(count > 0) {
+ ok_status(SecItemDelete(query), "%s: SecItemDelete %ld items", testName, count);
+ } else {
+ is(SecItemDelete(query), errSecItemNotFound, "%s: SecItemDelete no items", testName);
+ }
+ CFReleaseNull(query);
+}
+#define deleteItemsTests 1
+
+/* Checks in with securityd to see how many prompts were generated since the last call to this function, and tests against the number expected.
+ Returns the number generated since the last call. */
+static uint32_t checkPrompts(uint32_t expectedSinceLastCall, char* explanation) {
+ uint32_t currentPrompts = UINT_MAX;
+ uint32_t newPrompts = UINT_MAX;
+ ok_status(SecKeychainGetUserPromptAttempts(¤tPrompts), "%s: SecKeychainGetUserPromptAttempts", testName);
+
+ newPrompts = currentPrompts - promptAttempts;
+
+ is(newPrompts, expectedSinceLastCall, "%s: wrong number of prompts: %s", testName, explanation);
+ promptAttempts = currentPrompts;
+
+ return newPrompts;
+}
+#define checkPromptsTests 2
+
+#pragma clang diagnostic pop
+
+#endif /* kc_helpers_h */