]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/regressions/kc-24-key-copy-keychains.c
Security-57740.31.2.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / regressions / kc-24-key-copy-keychains.c
1 /*
2 * Copyright (c) 2016 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 xLicense.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import <Security/Security.h>
25 #include "keychain_regressions.h"
26 #include "kc-helpers.h"
27 #include "kc-key-helpers.h"
28 #include "kc-keychain-file-helpers.h"
29
30 #include <stdlib.h>
31 #include <err.h>
32
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <Security/Security.h>
35
36 /****************************************************************/
37
38 static OSStatus GenerateRSAKeyPair(
39 SecKeychainRef keychain,
40 CFStringRef keyLabel,
41 int keySizeValue,
42 Boolean *extractable,
43 SecKeyRef *publicKeyRef,
44 SecKeyRef *privateKeyRef)
45 {
46 OSStatus status;
47 CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
48
49 // create a SecAccessRef to set up the initial access control settings for this key
50 // (this step is optional; if omitted, the creating application has access to the key)
51 // note: the access descriptor should be the same string as will be used for the item's label,
52 // since it's the string that is displayed by the access confirmation dialog to describe the item.
53 SecAccessRef access = NULL;
54 status = SecAccessCreate(keyLabel, NULL, &access);
55
56 // create a dictionary of parameters describing the key we want to create
57 CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL, 0,
58 &kCFTypeDictionaryKeyCallBacks,
59 &kCFTypeDictionaryValueCallBacks);
60 /*
61 From the header doc for SecKeyGeneratePair (seems to be incomplete...):
62 * kSecAttrLabel default NULL
63 * kSecAttrIsPermanent if this key is present and has a Boolean
64 value of true, the key or key pair will be added to the default
65 keychain.
66 * kSecAttrApplicationTag default NULL
67 * kSecAttrEffectiveKeySize default NULL same as kSecAttrKeySizeInBits
68 * kSecAttrCanEncrypt default false for private keys, true for public keys
69 * kSecAttrCanDecrypt default true for private keys, false for public keys
70 * kSecAttrCanDerive default true
71 * kSecAttrCanSign default true for private keys, false for public keys
72 * kSecAttrCanVerify default false for private keys, true for public keys
73 * kSecAttrCanWrap default false for private keys, true for public keys
74 * kSecAttrCanUnwrap default true for private keys, false for public keys
75 */
76 CFDictionaryAddValue( params, kSecUseKeychain, keychain );
77 CFDictionaryAddValue( params, kSecAttrAccess, access );
78 CFDictionaryAddValue( params, kSecAttrKeyType, kSecAttrKeyTypeRSA );
79 CFDictionaryAddValue( params, kSecAttrKeySizeInBits, keySize );
80 CFDictionaryAddValue( params, kSecAttrIsPermanent, kCFBooleanTrue );
81
82 if (extractable)
83 CFDictionaryAddValue( params, kSecAttrIsExtractable, (*extractable) ? kCFBooleanTrue : kCFBooleanFalse );
84 if (keyLabel)
85 CFDictionaryAddValue( params, kSecAttrLabel, keyLabel );
86
87 // generate the key
88 status = SecKeyGeneratePair(params, publicKeyRef, privateKeyRef);
89
90 ok_status(status, "%s: SecKeyGeneratePair", testName);
91
92 if (params) CFRelease(params);
93 if (keychain) CFRelease(keychain);
94 if (access) CFRelease(access);
95
96 return status;
97 }
98
99 static SecAccessRef MakeNewAccess(SecKeychainItemRef item, CFStringRef accessLabel, Boolean allowAny)
100 {
101 OSStatus status;
102 SecAccessRef access = NULL;
103 CFMutableArrayRef trustedApplications = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
104
105 if (!allowAny) // use default access control ("confirm access")
106 {
107 // Make an exception list of applications you want to trust,
108 // which are allowed to access the item without requiring user confirmation.
109 // In this example, the calling app and Mail will have access.
110 SecTrustedApplicationRef myself = NULL, someOther = NULL;
111 status = SecTrustedApplicationCreateFromPath(NULL, &myself);
112 ok_status(status, "%s: MakeNewAccess: SecTrustedApplicationCreateFromPath (self)", testName);
113
114 if (!status && myself) {
115 CFArrayAppendValue(trustedApplications, myself);
116 CFRelease(myself);
117 }
118 status = SecTrustedApplicationCreateFromPath("/Applications/Mail.app", &someOther);
119 ok_status(status, "%s: MakeNewAccess: SecTrustedApplicationCreateFromPath (Mail.app)", testName);
120
121 if (!status && someOther) {
122 CFArrayAppendValue(trustedApplications, someOther);
123 CFRelease(someOther);
124 }
125 }
126
127 // If the keychain item already exists, use its access reference; otherwise, create a new one
128 if (item) {
129 status = SecKeychainItemCopyAccess(item, &access);
130 ok_status(status, "%s: MakeNewAccess: SecKeychainItemCopyAccess", testName);
131 } else {
132 status = SecAccessCreate(accessLabel, trustedApplications, &access);
133 ok_status(status, "%s: MakeNewAccess: SecAccessCreate", testName);
134 }
135 if (status) return NULL;
136
137 // get the access control list for decryption operations (this controls access to an item's data)
138 CFArrayRef aclList = NULL;
139 status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
140 ok_status(status, "%s: MakeNewAccess: SecAccessCopySelectedACLList", testName);
141 if (!status)
142 {
143 // get the first entry in the access control list
144 SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
145 CFArrayRef appList = NULL;
146 CFStringRef promptDescription = NULL;
147 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
148 status = SecACLCopySimpleContents(aclRef, &appList, &promptDescription, &promptSelector);
149 ok_status(status, "%s: MakeNewAccess: SecAccessCopySimpleContents", testName);
150
151 if (allowAny) // "allow all applications to access this item"
152 {
153 // change the decryption ACL to not require the passphrase, and have a NULL application list.
154 promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE;
155 status = SecACLSetSimpleContents(aclRef, NULL, promptDescription, &promptSelector);
156 ok_status(status, "%s: MakeNewAccess: SecACLSetSimpleContents", testName);
157 }
158 else // "allow access by these applications"
159 {
160 // modify the application list
161 status = SecACLSetSimpleContents(aclRef, trustedApplications, promptDescription, &promptSelector);
162 ok_status(status, "%s: MakeNewAccess: SecACLSetSimpleContents", testName);
163 }
164
165 if (appList) CFRelease(appList);
166 if (promptDescription) CFRelease(promptDescription);
167 }
168 if (aclList) CFRelease(aclList);
169 if (trustedApplications) CFRelease(trustedApplications);
170
171 return access;
172 }
173
174 static int testCopyKey(SecKeychainRef userKeychain, SecKeychainRef tempKeychain)
175 {
176 OSStatus status;
177 SecAccessRef access = NULL;
178 SecKeyRef publicKeyRef = NULL;
179 SecKeyRef privateKeyRef = NULL;
180 CFStringRef label = CFSTR("Test Key Copied To Keychain");
181
182 if (!tempKeychain) {
183 warnc(EXIT_FAILURE, "Failed to make a new temporary keychain!");
184 }
185
186 // generate key pair in temporary keychain
187 status = GenerateRSAKeyPair(tempKeychain,
188 label,
189 2048, // size
190 NULL, // implicitly extractable
191 &publicKeyRef,
192 &privateKeyRef);
193
194 if (status != errSecSuccess) {
195 warnc(EXIT_FAILURE, "Unable to get key pair (error %d)", (int)status);
196 }
197
198 // export private key from temp keychain to a wrapped data blob
199 CFDataRef exportedData = NULL;
200 CFStringRef tempPassword = CFSTR("MY_TEMPORARY_PASSWORD");
201
202 SecItemImportExportKeyParameters keyParams;
203 memset(&keyParams, 0, sizeof(keyParams));
204 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
205 keyParams.passphrase = tempPassword;
206
207 status = SecItemExport(privateKeyRef, kSecFormatWrappedPKCS8, 0, &keyParams, &exportedData);
208 ok_status(status, "%s: SecItemExport", testName);
209
210 if (!exportedData || status != noErr) {
211 warnc(EXIT_FAILURE, "Unable to export key! (error %d)", (int)status);
212 }
213
214 // set up an explicit access control instance for the imported key
215 // (this example allows unrestricted access to any application)
216 access = MakeNewAccess(NULL, label, true);
217 keyParams.accessRef = access;
218
219 // import wrapped data blob to user keychain
220 SecExternalFormat format = kSecFormatWrappedPKCS8;
221 SecExternalItemType itemType = kSecItemTypePrivateKey;
222
223 CFArrayRef importedItems = NULL;
224 status = SecItemImport(exportedData, NULL, &format, &itemType, 0, &keyParams, userKeychain, &importedItems);
225 ok_status(status, "%s: SecItemImport", testName);
226
227 if (status != noErr) {
228 warnc(EXIT_FAILURE, "Unable to import key! (error %d)", (int)status);
229 }
230 if (importedItems) {
231
232 // make sure to set a label on our newly imported key, since a label is not part of the PKCS8 format.
233 SecKeyRef importedKey = (SecKeyRef) CFArrayGetValueAtIndex(importedItems, 0);
234 if (CFGetTypeID(importedKey) == SecKeyGetTypeID()) {
235 // set up a query defining the item(s) to be operated on, in this case, one item uniquely identified by reference
236 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
237 &kCFTypeDictionaryValueCallBacks);
238 CFDictionaryAddValue( query, kSecClass, kSecClassKey ); // item class is a required attribute in any query
239 CFDictionaryAddValue( query, kSecValueRef, importedKey );
240
241 // define the attributes to be updated, in this case the label
242 CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
243 &kCFTypeDictionaryValueCallBacks);
244 CFDictionaryAddValue( attrs, kSecAttrLabel, label );
245
246 // do the update
247 status = SecItemUpdate( query, attrs );
248 ok_status(status, "%s: SecItemUpdate", testName);
249
250 if (status != errSecSuccess) {
251 warnc(EXIT_FAILURE, "Failed to update label of imported key! (error %d)", (int)status);
252 }
253
254 CFRelease(query);
255 CFRelease(attrs);
256 }
257 CFRelease(importedItems);
258 }
259
260 // ensure that key was copied, and its label changed
261 checkN(testName, makeQueryKeyDictionaryWithLabel(userKeychain, kSecAttrKeyClassPrivate, label), 1);
262
263 if (access) CFRelease(access);
264
265 return 0;
266 }
267
268
269 int kc_24_key_copy_keychain(int argc, char *const *argv)
270 {
271 plan_tests(18);
272 initializeKeychainTests(__FUNCTION__);
273
274 SecKeychainRef keychain = getPopulatedTestKeychain();
275 SecKeychainRef blankKeychain = createNewKeychain("forKeys", "password");
276
277 testCopyKey(keychain, blankKeychain);
278
279 ok_status(SecKeychainDelete(keychain), "%s: SecKeychainDelete", testName);
280 ok_status(SecKeychainDelete(blankKeychain), "%s: SecKeychainDelete", testName);
281
282 CFReleaseNull(keychain);
283 CFReleaseNull(blankKeychain);
284
285 checkPrompts(0, "No prompts while importing items");
286
287 deleteTestFiles();
288 return 0;
289 }
290