]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * extendAttrTest.cpp | |
3 | */ | |
4 | ||
5 | #include <stdlib.h> | |
6 | #include <strings.h> | |
7 | #include <stdio.h> | |
8 | #include <unistd.h> | |
9 | #include <Security/SecKeychainItemExtendedAttributes.h> | |
10 | #include <Security/Security.h> | |
11 | #include <security_cdsa_utils/cuFileIo.h> | |
12 | #include <utilLib/common.h> | |
13 | ||
14 | #define DEFAULT_KC_NAME "extendAttr.keychain" | |
15 | ||
16 | static void usage(char **argv) | |
17 | { | |
18 | printf("usage: %s [options]\n", argv[0]); | |
19 | printf("Options:\n"); | |
20 | printf(" -k keychain -- default is %s\n", DEFAULT_KC_NAME); | |
21 | printf(" -n -- don't delete attributes or keychain\n"); | |
22 | printf(" -q -- quiet\n"); | |
23 | exit(1); | |
24 | } | |
25 | ||
26 | /* RSA keys, both in OpenSSL format */ | |
27 | #define PUB_KEY "rsakey_pub.der" | |
28 | #define PRIV_KEY "rsakey_priv.der" | |
29 | #define CERT_FILE "amazon_v3.100.cer" | |
30 | #define PWD_SERVICE "some service" | |
31 | #define PWD_ACCOUNT "some account" | |
32 | #define PWD_PWD "some password" | |
33 | ||
34 | /* set up unique extended attributes for each tested item */ | |
35 | typedef struct { | |
36 | CFStringRef attr1Name; | |
37 | const char *attr1Value; | |
38 | CFStringRef attr2Name; | |
39 | const char *attr2Value; | |
40 | } ItemAttrs; | |
41 | ||
42 | static const ItemAttrs pubKeyAttrs = { | |
43 | CFSTR("one pub key Attribute"), | |
44 | "some pub key value", | |
45 | CFSTR("another pub key Attribute"), | |
46 | "another pub key value" | |
47 | }; | |
48 | ||
49 | static const ItemAttrs privKeyAttrs = { | |
50 | CFSTR("one priv key Attribute"), | |
51 | "some priv key value", | |
52 | CFSTR("another priv key Attribute"), | |
53 | "another priv key value" | |
54 | }; | |
55 | ||
56 | static const ItemAttrs certAttrs = { | |
57 | CFSTR("one cert Attribute"), | |
58 | "some cert value", | |
59 | CFSTR("another cert Attribute"), | |
60 | "another cert value" | |
61 | }; | |
62 | ||
63 | static const ItemAttrs pwdAttrs = { | |
64 | CFSTR("one pwd Attribute"), | |
65 | "some pwd value", | |
66 | CFSTR("another pwd Attribute"), | |
67 | "another pwd value" | |
68 | }; | |
69 | ||
70 | #define CFRELEASE(cf) if(cf) { CFRelease(cf); } | |
71 | ||
72 | /* import file as key into specified keychain */ | |
73 | static int doImportKey( | |
74 | const char *fileName, | |
75 | SecExternalFormat format, | |
76 | SecExternalItemType itemType, | |
77 | SecKeychainRef kcRef, | |
78 | SecKeyRef *keyRef) // RETURNED | |
79 | { | |
80 | unsigned char *item = NULL; | |
81 | unsigned itemLen = 0; | |
82 | ||
83 | if(readFile(fileName, &item, &itemLen)) { | |
84 | printf("***Error reading %s. \n", fileName); | |
85 | } | |
86 | CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)item, itemLen); | |
87 | free(item); | |
88 | SecKeyImportExportParameters params; | |
89 | memset(¶ms, 0, sizeof(params)); | |
90 | params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; | |
91 | params.keyUsage = CSSM_KEYUSE_ANY; | |
92 | params.keyAttributes = CSSM_KEYATTR_PERMANENT; | |
93 | if(itemType == kSecItemTypePrivateKey) { | |
94 | params.keyAttributes |= CSSM_KEYATTR_SENSITIVE; | |
95 | } | |
96 | CFArrayRef outArray = NULL; | |
97 | OSStatus ortn; | |
98 | ortn = SecKeychainItemImport(cfd, NULL, &format, &itemType, 0, ¶ms, kcRef, &outArray); | |
99 | if(ortn) { | |
100 | cssmPerror("SecKeychainItemImport", ortn); | |
101 | } | |
102 | CFRelease(cfd); | |
103 | if(ortn) { | |
104 | return -1; | |
105 | } | |
106 | if((outArray == NULL) || (CFArrayGetCount(outArray) == 0)) { | |
107 | printf("SecKeychainItemImport succeeded, but no returned items\n"); | |
108 | return -1; | |
109 | } | |
110 | *keyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); | |
111 | if(CFGetTypeID(*keyRef) != SecKeyGetTypeID()) { | |
112 | printf("***Unknown type returned after import\n"); | |
113 | return -1; | |
114 | } | |
115 | CFRetain(*keyRef); | |
116 | CFRelease(outArray); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | /* import file as cert into specified keychain */ | |
121 | static int doImportCert( | |
122 | const char *fileName, | |
123 | SecKeychainRef kcRef, | |
124 | SecCertificateRef *certRef) // RETURNED | |
125 | { | |
126 | unsigned char *item = NULL; | |
127 | unsigned itemLen = 0; | |
128 | ||
129 | if(readFile(fileName, &item, &itemLen)) { | |
130 | printf("***Error reading %s. \n", fileName); | |
131 | return -1; | |
132 | } | |
133 | CSSM_DATA certData = {itemLen, (uint8 *)item}; | |
134 | OSStatus ortn = SecCertificateCreateFromData(&certData, | |
135 | CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, certRef); | |
136 | if(ortn) { | |
137 | cssmPerror("SecCertificateCreateFromData", ortn); | |
138 | return -1; | |
139 | } | |
140 | ortn = SecCertificateAddToKeychain(*certRef, kcRef); | |
141 | if(ortn) { | |
142 | cssmPerror("SecCertificateAddToKeychain", ortn); | |
143 | return -1; | |
144 | } | |
145 | return 0; | |
146 | } | |
147 | ||
148 | /* | |
149 | * Verify specified attr does not exist | |
150 | * set it | |
151 | * make sure we get it back | |
152 | */ | |
153 | int testOneAttr( | |
154 | SecKeychainItemRef itemRef, | |
155 | CFStringRef attrName, | |
156 | CFDataRef attrVal, | |
157 | bool quiet) | |
158 | { | |
159 | OSStatus ortn; | |
160 | CFDataRef fetchedVal = NULL; | |
161 | int ourRtn = 0; | |
162 | ||
163 | if(!quiet) { | |
164 | printf(" ...verifying attribute doesn't exist\n"); | |
165 | } | |
166 | ortn = SecKeychainItemCopyExtendedAttribute(itemRef, attrName, &fetchedVal); | |
167 | if(ortn != errSecNoSuchAttr) { | |
168 | printf("***First SecKeychainItemCopyExtendedAttribute returned %d, expected %d\n", | |
169 | (int)ortn, (int)errSecNoSuchAttr); | |
170 | ourRtn = -1; | |
171 | goto errOut; | |
172 | } | |
173 | if(!quiet) { | |
174 | printf(" ...setting attribute\n"); | |
175 | } | |
176 | ortn = SecKeychainItemSetExtendedAttribute(itemRef, attrName, attrVal); | |
177 | if(ortn) { | |
178 | cssmPerror("SecKeychainItemSetExtendedAttribute", ortn); | |
179 | ourRtn = -1; | |
180 | goto errOut; | |
181 | } | |
182 | if(!quiet) { | |
183 | printf(" ...verify attribute\n"); | |
184 | } | |
185 | ortn = SecKeychainItemCopyExtendedAttribute(itemRef, attrName, &fetchedVal); | |
186 | if(ortn) { | |
187 | cssmPerror("SecKeychainItemCopyExtendedAttribute", ortn); | |
188 | ourRtn = -1; | |
189 | goto errOut; | |
190 | } | |
191 | if(!CFEqual(fetchedVal, attrVal)) { | |
192 | printf("***Mismatch in set and fetched attribute\n"); | |
193 | ourRtn = -1; | |
194 | } | |
195 | errOut: | |
196 | CFRELEASE(fetchedVal); | |
197 | return ourRtn; | |
198 | } | |
199 | ||
200 | /* | |
201 | * Set two distinct extended attributes; | |
202 | * Ensure that each comes back via SecKeychainItemCopyExtendedAttribute(); | |
203 | * Ensure that both come back via SecKeychainItemCopyAllExtendedAttributes(); | |
204 | */ | |
205 | int doTest(SecKeychainItemRef itemRef, | |
206 | const ItemAttrs &itemAttrs, | |
207 | bool quiet) | |
208 | { | |
209 | CFDataRef attrVal1 = CFDataCreate(NULL, | |
210 | (const UInt8 *)itemAttrs.attr1Value, strlen(itemAttrs.attr1Value)); | |
211 | if(testOneAttr(itemRef, itemAttrs.attr1Name, attrVal1, quiet)) { | |
212 | return -1; | |
213 | } | |
214 | CFDataRef attrVal2 = CFDataCreate(NULL, | |
215 | (const UInt8 *)itemAttrs.attr2Value, strlen(itemAttrs.attr2Value)); | |
216 | if(testOneAttr(itemRef, itemAttrs.attr2Name, attrVal2, quiet)) { | |
217 | return -1; | |
218 | } | |
219 | ||
220 | if(!quiet) { | |
221 | printf(" ...verify both attributes via CopyAllExtendedAttributes()\n"); | |
222 | } | |
223 | /* make sure they both come back in SecKeychainItemCopyAllExtendedAttributes */ | |
224 | CFArrayRef attrNames = NULL; | |
225 | CFArrayRef attrValues = NULL; | |
226 | OSStatus ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, &attrValues); | |
227 | if(ortn) { | |
228 | cssmPerror("SecKeychainItemCopyAllExtendedAttributes", ortn); | |
229 | return -1; | |
230 | } | |
231 | CFIndex numNames = CFArrayGetCount(attrNames); | |
232 | CFIndex numValues = CFArrayGetCount(attrValues); | |
233 | if((numNames != 2) || (numValues != 2)) { | |
234 | printf("***Bad array count after SecKeychainItemCopyAllExtendedAttributes\n"); | |
235 | printf(" numNames %ld numValues %ld; expected 2 for both\n", | |
236 | (long)numNames, (long)numValues); | |
237 | return -1; | |
238 | } | |
239 | bool found1 = false; | |
240 | bool found2 = false; | |
241 | for(CFIndex dex=0; dex<numNames; dex++) { | |
242 | CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex); | |
243 | CFDataRef valToCompare = NULL; | |
244 | if(CFEqual(attrName, itemAttrs.attr1Name)) { | |
245 | found1 = true; | |
246 | valToCompare = attrVal1; | |
247 | } | |
248 | else if(CFEqual(attrName, itemAttrs.attr2Name)) { | |
249 | found2 = true; | |
250 | valToCompare = attrVal2; | |
251 | } | |
252 | else { | |
253 | printf("***Found unknown attribute name\n"); | |
254 | return -1; | |
255 | } | |
256 | CFDataRef foundVal = (CFDataRef)CFArrayGetValueAtIndex(attrValues, dex); | |
257 | if(!CFEqual(foundVal, valToCompare)) { | |
258 | printf("***Attribute Value miscompare\n"); | |
259 | return -1; | |
260 | } | |
261 | } | |
262 | CFRelease(attrNames); | |
263 | CFRelease(attrValues); | |
264 | CFRelease(attrVal1); | |
265 | CFRelease(attrVal2); | |
266 | ||
267 | if(!found1 || !found2) { | |
268 | printf("***wrote two attribute; found1 %s, found2 %s\n", | |
269 | found1 ? "true" : "false", found2 ? "true" : "false"); | |
270 | return 1; | |
271 | } | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | /* delete two attrs, verify that none are left */ | |
277 | static int doDeleteTest( | |
278 | SecKeychainItemRef itemRef, | |
279 | const ItemAttrs &itemAttrs, | |
280 | bool quiet) | |
281 | { | |
282 | if(!quiet) { | |
283 | printf(" ...deleting both attributes, verifying none are left\n"); | |
284 | } | |
285 | ||
286 | OSStatus ortn = SecKeychainItemSetExtendedAttribute(itemRef, itemAttrs.attr1Name, NULL); | |
287 | if(ortn) { | |
288 | cssmPerror("SecKeychainItemSetExtendedAttribute (NULL)", ortn); | |
289 | return -1; | |
290 | } | |
291 | ortn = SecKeychainItemSetExtendedAttribute(itemRef, itemAttrs.attr2Name, NULL); | |
292 | if(ortn) { | |
293 | cssmPerror("SecKeychainItemSetExtendedAttribute (NULL)", ortn); | |
294 | return -1; | |
295 | } | |
296 | CFArrayRef attrNames = NULL; | |
297 | CFArrayRef attrValues = NULL; | |
298 | ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, &attrValues); | |
299 | if(ortn != errSecNoSuchAttr) { | |
300 | printf("***Last SecKeychainItemCopyExtendedAttribute returned %d, expected %d\n", | |
301 | (int)ortn, (int)errSecNoSuchAttr); | |
302 | return -1; | |
303 | } | |
304 | return 0; | |
305 | } | |
306 | ||
307 | /* | |
308 | * Verify that SecKeychainItemDelete() also deletes extended attributes. | |
309 | * | |
310 | * Assuming empty keychain: | |
311 | * Import a cert; | |
312 | * Set two extended attributes, make sure they're there; | |
313 | * Delete the cert; | |
314 | * Import the cert again; | |
315 | * Verify that the new item has *no* extended attributes; | |
316 | */ | |
317 | static int doDeleteItemTest( | |
318 | SecKeychainRef kcRef, | |
319 | bool quiet) | |
320 | { | |
321 | SecCertificateRef certRef = NULL; | |
322 | ||
323 | if(doImportCert(CERT_FILE, kcRef, &certRef)) { | |
324 | return 1; | |
325 | } | |
326 | if(!quiet) { | |
327 | printf("...testing cert\n"); | |
328 | } | |
329 | if(doTest((SecKeychainItemRef)certRef, certAttrs, quiet)) { | |
330 | return -1; | |
331 | } | |
332 | ||
333 | /* doTest() verified that there are two extended attrs */ | |
334 | if(!quiet) { | |
335 | printf("...deleting cert\n"); | |
336 | } | |
337 | OSStatus ortn = SecKeychainItemDelete((SecKeychainItemRef)certRef); | |
338 | if(ortn) { | |
339 | cssmPerror("SecKeychainItemDelete", ortn); | |
340 | return -1; | |
341 | } | |
342 | CFRelease(certRef); | |
343 | ||
344 | if(!quiet) { | |
345 | printf("...reimporting cert, verifying it has no extended attributes\n"); | |
346 | } | |
347 | if(doImportCert(CERT_FILE, kcRef, &certRef)) { | |
348 | return 1; | |
349 | } | |
350 | CFArrayRef attrNames = NULL; | |
351 | ortn = SecKeychainItemCopyAllExtendedAttributes((SecKeychainItemRef)certRef, &attrNames, | |
352 | NULL); | |
353 | if(ortn != errSecNoSuchAttr) { | |
354 | printf("***Deleted cert, re-imported it, and the new cert has extended attributes!\n"); | |
355 | return -1; | |
356 | } | |
357 | CFRelease(certRef); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | int main(int argc, char **argv) | |
362 | { | |
363 | const char *kcName = DEFAULT_KC_NAME; | |
364 | extern char *optarg; | |
365 | int arg; | |
366 | bool quiet = false; | |
367 | bool noDelete = false; | |
368 | ||
369 | while ((arg = getopt(argc, argv, "k:qnh")) != -1) { | |
370 | switch (arg) { | |
371 | case 'k': | |
372 | kcName = optarg; | |
373 | break; | |
374 | case 'n': | |
375 | noDelete = true; | |
376 | break; | |
377 | case 'q': | |
378 | quiet = true; | |
379 | break; | |
380 | case 'h': | |
381 | usage(argv); | |
382 | } | |
383 | } | |
384 | if(optind != argc) { | |
385 | usage(argv); | |
386 | } | |
387 | ||
388 | testStartBanner("extendAttrTest", argc, argv); | |
389 | ||
390 | SecKeychainRef kcRef = NULL; | |
391 | OSStatus ortn; | |
392 | ||
393 | if(!quiet) { | |
394 | printf("Deleting possible existing keychain and creating %s...\n", kcName); | |
395 | ||
396 | } | |
397 | /* delete possible existing keychain, then create it */ | |
398 | if (SecKeychainOpen(kcName, &kcRef) == noErr) | |
399 | { | |
400 | SecKeychainDelete(kcRef); | |
401 | CFRelease(kcRef); | |
402 | } | |
403 | ||
404 | kcRef = NULL; | |
405 | ortn = SecKeychainCreate(kcName, | |
406 | strlen(DEFAULT_KC_NAME), DEFAULT_KC_NAME, | |
407 | false, NULL, &kcRef); | |
408 | if(ortn) { | |
409 | cssmPerror("SecKeychainCreate", ortn); | |
410 | exit(1); | |
411 | } | |
412 | ||
413 | /* import keys */ | |
414 | SecKeyRef pubKey = NULL; | |
415 | SecKeyRef privKey = NULL; | |
416 | if(!quiet) { | |
417 | printf("Importing %s to keychain...\n", PUB_KEY); | |
418 | } | |
419 | if(doImportKey(PUB_KEY, kSecFormatOpenSSL, kSecItemTypePublicKey, kcRef, &pubKey)) { | |
420 | exit(1); | |
421 | } | |
422 | if(!quiet) { | |
423 | printf("Importing %s to keychain...\n", PRIV_KEY); | |
424 | } | |
425 | if(doImportKey(PRIV_KEY, kSecFormatOpenSSL, kSecItemTypePrivateKey, kcRef, &privKey)) { | |
426 | exit(1); | |
427 | } | |
428 | ||
429 | if(!quiet) { | |
430 | printf("...testing public key\n"); | |
431 | } | |
432 | if(doTest((SecKeychainItemRef)pubKey, pubKeyAttrs, quiet)) { | |
433 | return -1; | |
434 | } | |
435 | if(!quiet) { | |
436 | printf("...testing private key\n"); | |
437 | } | |
438 | if(doTest((SecKeychainItemRef)privKey, privKeyAttrs, quiet)) { | |
439 | return -1; | |
440 | } | |
441 | ||
442 | /* | |
443 | * Those keys and their extended attrs are still in the keychain. Test a cert. | |
444 | */ | |
445 | SecCertificateRef certRef = NULL; | |
446 | if(doImportCert(CERT_FILE, kcRef, &certRef)) { | |
447 | exit(1); | |
448 | } | |
449 | if(!quiet) { | |
450 | printf("...testing cert\n"); | |
451 | } | |
452 | if(doTest((SecKeychainItemRef)certRef, certAttrs, quiet)) { | |
453 | return -1; | |
454 | } | |
455 | ||
456 | /* leaving everything in place, test a generic password. */ | |
457 | SecKeychainItemRef pwdRef = NULL; | |
458 | ortn = SecKeychainAddGenericPassword(kcRef, | |
459 | strlen(PWD_SERVICE), PWD_SERVICE, | |
460 | strlen(PWD_ACCOUNT), PWD_ACCOUNT, | |
461 | strlen(PWD_PWD), PWD_PWD, | |
462 | &pwdRef); | |
463 | if(ortn) { | |
464 | cssmPerror("SecKeychainAddGenericPassword", ortn); | |
465 | exit(1); | |
466 | } | |
467 | if(!quiet) { | |
468 | printf("...testing generic password\n"); | |
469 | } | |
470 | if(doTest(pwdRef, pwdAttrs, quiet)) { | |
471 | return -1; | |
472 | } | |
473 | ||
474 | if(noDelete) { | |
475 | goto done; | |
476 | } | |
477 | ||
478 | /* delete extended attrs; make sure they really get deleted */ | |
479 | if(!quiet) { | |
480 | printf("...removing extended attributes from public key\n"); | |
481 | } | |
482 | if(doDeleteTest((SecKeychainItemRef)pubKey, pubKeyAttrs, quiet)) { | |
483 | exit(1); | |
484 | } | |
485 | if(!quiet) { | |
486 | printf("...removing extended attributes from private key\n"); | |
487 | } | |
488 | if(doDeleteTest((SecKeychainItemRef)privKey, privKeyAttrs, quiet)) { | |
489 | exit(1); | |
490 | } | |
491 | if(!quiet) { | |
492 | printf("...removing extended attributes from certificate\n"); | |
493 | } | |
494 | if(doDeleteTest((SecKeychainItemRef)certRef, certAttrs, quiet)) { | |
495 | exit(1); | |
496 | } | |
497 | if(!quiet) { | |
498 | printf("...removing extended attributes from generic password\n"); | |
499 | } | |
500 | if(doDeleteTest(pwdRef, pwdAttrs, quiet)) { | |
501 | exit(1); | |
502 | } | |
503 | ||
504 | CFRelease(pubKey); | |
505 | CFRelease(privKey); | |
506 | CFRelease(pwdRef); | |
507 | ||
508 | /* Verify that SecKeychainItemDelete() also deletes extended attributes */ | |
509 | ortn = SecKeychainItemDelete((SecKeychainItemRef)certRef); | |
510 | if(ortn) { | |
511 | cssmPerror("SecKeychainItemDelete", ortn); | |
512 | exit(1); | |
513 | } | |
514 | CFRelease(certRef); | |
515 | if(doDeleteItemTest(kcRef, quiet)) { | |
516 | exit(1); | |
517 | } | |
518 | ||
519 | SecKeychainDelete(kcRef); | |
520 | CFRelease(kcRef); | |
521 | done: | |
522 | if(!quiet) { | |
523 | printf("...Success\n"); | |
524 | } | |
525 | return 0; | |
526 | } |