2 * Copyright (c) 2003-2009,2012,2014 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@
23 * keychain_utilities.c
26 #include "keychain_utilities.h"
29 #include <Security/cssmapi.h>
30 #include <Security/SecAccess.h>
31 #include <Security/SecACL.h>
32 #include <Security/SecTrustedApplication.h>
33 #include <Security/SecKeychainItem.h>
36 #include <sys/param.h>
37 #include <libkern/OSByteOrder.h>
41 // SecTrustedApplicationValidateWithPath
42 #include <Security/SecTrustedApplicationPriv.h>
45 void check_obsolete_keychain(const char *kcName
)
50 if(!strcmp(kcName
, "/System/Library/Keychains/X509Anchors")) {
51 fprintf(stderr
, "***************************************************************\n");
52 fprintf(stderr
, " WARNING\n");
53 fprintf(stderr
, "\n");
54 fprintf(stderr
, "The keychain you are accessing, X509Anchors, is no longer\n");
55 fprintf(stderr
, "used by Mac OS X as the system root certificate store.\n");
56 fprintf(stderr
, "Please read the security man page for information on the \n");
57 fprintf(stderr
, "add-trusted-cert command. New system root certificates should\n");
58 fprintf(stderr
, "be added to the Admin Trust Settings domain and to the \n");
59 fprintf(stderr
, "System keychain in /Library/Keychains.\n");
60 fprintf(stderr
, "***************************************************************\n");
62 else if(!strcmp(kcName
, "/System/Library/Keychains/X509Certificates")) {
63 fprintf(stderr
, "***************************************************************\n");
64 fprintf(stderr
, " WARNING\n");
65 fprintf(stderr
, "\n");
66 fprintf(stderr
, "The keychain you are accessing, X509Certificates, is no longer\n");
67 fprintf(stderr
, "used by Mac OS X as the system intermediate certificate\n");
68 fprintf(stderr
, "store. New system intermediate certificates should be added\n");
69 fprintf(stderr
, "to the System keychain in /Library/Keychains.\n");
70 fprintf(stderr
, "***************************************************************\n");
75 keychain_open(const char *name
)
77 SecKeychainRef keychain
= NULL
;
80 check_obsolete_keychain(name
);
81 if (name
&& name
[0] != '/')
83 CFArrayRef dynamic
= NULL
;
84 result
= SecKeychainCopyDomainSearchList(
85 kSecPreferencesDomainDynamic
, &dynamic
);
88 sec_error("SecKeychainCopyDomainSearchList %s: %s",
89 name
, sec_errstr(result
));
95 uint32_t count
= dynamic
? CFArrayGetCount(dynamic
) : 0;
97 for (i
= 0; i
< count
; ++i
)
99 char pathName
[MAXPATHLEN
];
100 UInt32 ioPathLength
= sizeof(pathName
);
101 bzero(pathName
, ioPathLength
);
102 keychain
= (SecKeychainRef
)CFArrayGetValueAtIndex(dynamic
, i
);
103 result
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
106 sec_error("SecKeychainGetPath %s: %s",
107 name
, sec_errstr(result
));
110 if (!strncmp(pathName
, name
, ioPathLength
))
121 result
= SecKeychainOpen(name
, &keychain
);
124 sec_error("SecKeychainOpen %s: %s", name
, sec_errstr(result
));
131 keychain_create_array(int argc
, char * const *argv
)
136 return keychain_open(argv
[0]);
139 CFMutableArrayRef keychains
= CFArrayCreateMutable(NULL
, argc
, &kCFTypeArrayCallBacks
);
141 for (ix
= 0; ix
< argc
; ++ix
)
143 SecKeychainRef keychain
= keychain_open(argv
[ix
]);
146 CFArrayAppendValue(keychains
, keychain
);
156 parse_fourcharcode(const char *name
, UInt32
*code
)
159 int len
= (name
) ? strlen(name
) : 0;
161 // error check the name
164 fprintf(stderr
, "Error: four-character types must be exactly 4 characters long.\n");
166 fprintf(stderr
, "(Try \"%s \" instead of \"%s\")\n", name
, name
);
172 for (i
= 0; i
< 4; ++i
)
174 cc
= (cc
<< 8) | name
[i
];
177 *code
= cc
; // note: this is in host byte order, suitable for passing to APIs
183 print_keychain_name(FILE *stream
, SecKeychainRef keychain
)
186 char pathName
[MAXPATHLEN
];
187 UInt32 ioPathLength
= sizeof(pathName
);
188 OSStatus status
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
191 sec_perror("SecKeychainGetPath", status
);
196 print_buffer(stream
, ioPathLength
, pathName
);
203 print_cfdata(FILE *stream
, CFDataRef data
)
206 return print_buffer(stream
, CFDataGetLength(data
), CFDataGetBytePtr(data
));
208 fprintf(stream
, "<NULL>");
212 print_cfstring(FILE *stream
, CFStringRef string
)
215 fprintf(stream
, "<NULL>");
218 const char *utf8
= CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
);
220 fprintf(stream
, "%s", utf8
);
223 CFRange rangeToProcess
= CFRangeMake(0, CFStringGetLength(string
));
224 while (rangeToProcess
.length
> 0)
226 UInt8 localBuffer
[256];
227 CFIndex usedBufferLength
;
228 CFIndex numChars
= CFStringGetBytes(string
, rangeToProcess
,
229 kCFStringEncodingUTF8
, '?', FALSE
, localBuffer
,
230 sizeof(localBuffer
), &usedBufferLength
);
232 break; // Failed to convert anything...
234 fprintf(stream
, "%.*s", (int)usedBufferLength
, localBuffer
);
235 rangeToProcess
.location
+= numChars
;
236 rangeToProcess
.length
-= numChars
;
243 print_access(FILE *stream
, SecAccessRef access
, Boolean interactive
)
245 CFArrayRef aclList
= NULL
;
246 CFIndex aclix
, aclCount
;
250 status
= SecAccessCopyACLList(access
, &aclList
);
253 sec_perror("SecAccessCopyACLList", status
);
258 aclCount
= CFArrayGetCount(aclList
);
259 fprintf(stream
, "access: %lu entries\n", aclCount
);
260 for (aclix
= 0; aclix
< aclCount
; ++aclix
)
262 CFArrayRef applicationList
= NULL
;
263 CFStringRef description
= NULL
;
264 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
= {};
265 CFIndex appix
, appCount
;
267 SecACLRef acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, aclix
);
268 CSSM_ACL_AUTHORIZATION_TAG tags
[64]; // Pick some upper limit
269 uint32 tagix
, tagCount
= sizeof(tags
) / sizeof(*tags
);
270 status
= SecACLGetAuthorizations(acl
, tags
, &tagCount
);
273 sec_perror("SecACLGetAuthorizations", status
);
278 fprintf(stream
, " entry %lu:\n authorizations (%lu):", aclix
,
279 (unsigned long)tagCount
);
280 for (tagix
= 0; tagix
< tagCount
; ++tagix
)
282 CSSM_ACL_AUTHORIZATION_TAG tag
= tags
[tagix
];
285 case CSSM_ACL_AUTHORIZATION_ANY
:
286 fputs(" any", stream
);
288 case CSSM_ACL_AUTHORIZATION_LOGIN
:
289 fputs(" login", stream
);
291 case CSSM_ACL_AUTHORIZATION_GENKEY
:
292 fputs(" genkey", stream
);
294 case CSSM_ACL_AUTHORIZATION_DELETE
:
295 fputs(" delete", stream
);
297 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
:
298 fputs(" export_wrapped", stream
);
300 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
:
301 fputs(" export_clear", stream
);
303 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
:
304 fputs(" import_wrapped", stream
);
306 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR
:
307 fputs(" import_clear", stream
);
309 case CSSM_ACL_AUTHORIZATION_SIGN
:
310 fputs(" sign", stream
);
312 case CSSM_ACL_AUTHORIZATION_ENCRYPT
:
313 fputs(" encrypt", stream
);
315 case CSSM_ACL_AUTHORIZATION_DECRYPT
:
316 fputs(" decrypt", stream
);
318 case CSSM_ACL_AUTHORIZATION_MAC
:
319 fputs(" mac", stream
);
321 case CSSM_ACL_AUTHORIZATION_DERIVE
:
322 fputs(" derive", stream
);
324 case CSSM_ACL_AUTHORIZATION_DBS_CREATE
:
325 fputs(" dbs_create", stream
);
327 case CSSM_ACL_AUTHORIZATION_DBS_DELETE
:
328 fputs(" dbs_delete", stream
);
330 case CSSM_ACL_AUTHORIZATION_DB_READ
:
331 fputs(" db_read", stream
);
333 case CSSM_ACL_AUTHORIZATION_DB_INSERT
:
334 fputs(" db_insert", stream
);
336 case CSSM_ACL_AUTHORIZATION_DB_MODIFY
:
337 fputs(" db_modify", stream
);
339 case CSSM_ACL_AUTHORIZATION_DB_DELETE
:
340 fputs(" db_delete", stream
);
342 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL
:
343 fputs(" change_acl", stream
);
345 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER
:
346 fputs(" change_owner", stream
);
349 fprintf(stream
, " tag=%lu", (unsigned long)tag
);
355 status
= SecACLCopySimpleContents(acl
, &applicationList
, &description
, &promptSelector
);
358 sec_perror("SecACLCopySimpleContents", status
);
362 if (promptSelector
.flags
& CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE
)
363 fputs(" require-password\n", stream
);
365 fputs(" don't-require-password\n", stream
);
367 fputs(" description: ", stream
);
368 print_cfstring(stream
, description
);
373 appCount
= CFArrayGetCount(applicationList
);
374 fprintf(stream
, " applications (%lu):\n", appCount
);
379 fprintf(stream
, " applications: <null>\n");
382 for (appix
= 0; appix
< appCount
; ++appix
)
385 SecTrustedApplicationRef app
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(applicationList
, appix
);
386 CFDataRef data
= NULL
;
387 fprintf(stream
, " %lu: ", appix
);
388 status
= SecTrustedApplicationCopyData(app
, &data
);
391 sec_perror("SecTrustedApplicationCopyData", status
);
395 bytes
= CFDataGetBytePtr(data
);
396 if (bytes
&& bytes
[0] == 0x2f) {
397 fprintf(stream
, "%s", (const char *)bytes
);
398 if ((status
= SecTrustedApplicationValidateWithPath(app
, (const char *)bytes
)) == noErr
) {
399 fprintf(stream
, " (OK)");
401 fprintf(stream
, " (status %d)", (int)status
);
403 fprintf(stream
, "\n");
405 print_cfdata(stream
, data
);
413 CFRelease(applicationList
);
416 CFRelease(description
);
420 char buffer
[10] = {};
421 fprintf(stderr
, "Remove this acl? ");
422 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
424 fprintf(stderr
, "removing acl\n");
425 status
= SecACLRemove(acl
);
428 sec_perror("SecACLRemove", status
);
443 print_keychain_item_attributes(FILE *stream
, SecKeychainItemRef item
, Boolean show_data
, Boolean show_raw_data
, Boolean show_acl
, Boolean interactive
)
448 SecKeychainRef keychain
= NULL
;
449 SecAccessRef access
= NULL
;
450 SecItemClass itemClass
= 0;
452 SecKeychainAttributeList
*attrList
= NULL
;
453 SecKeychainAttributeInfo
*info
= NULL
;
457 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
460 sec_perror("SecKeychainItemCopyKeychain", status
);
465 fputs("keychain: ", stream
);
466 result
= print_keychain_name(stream
, keychain
);
471 /* First find out the item class. */
472 status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
475 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
480 fputs("class: ", stream
);
482 buffer
[3] = itemClass
& 0xFF;
483 buffer
[2] = (itemClass
>> 8) & 0xFF;
484 buffer
[1] = (itemClass
>> 16) & 0xFF;
485 buffer
[0] = (itemClass
>> 24) & 0xFF;
487 print_buffer(stream
, 4, buffer
);
488 fputs("\nattributes:\n", stream
);
492 case kSecInternetPasswordItemClass
:
493 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
495 case kSecGenericPasswordItemClass
:
496 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
498 case 'ashp': /* kSecAppleSharePasswordItemClass */
499 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
506 /* Now get the AttributeInfo for it. */
507 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
510 sec_perror("SecKeychainAttributeInfoForItemID", status
);
515 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
,
516 show_data
? &length
: NULL
,
517 show_data
? &data
: NULL
);
520 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
525 if (info
->count
!= attrList
->count
)
527 sec_error("info count: %ld != attribute count: %ld", info
->count
, attrList
->count
);
532 for (ix
= 0; ix
< info
->count
; ++ix
)
534 UInt32 tag
= info
->tag
[ix
];
535 UInt32 format
= info
->format
[ix
];
536 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
537 if (tag
!= attribute
->tag
)
539 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix
, info
->count
, tag
, attribute
->tag
);
545 print_uint32(stream
, tag
);
548 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
549 fputs("<string>", stream
);
551 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
552 fputs("<sint32>", stream
);
554 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
555 fputs("<uint32>", stream
);
557 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
:
558 fputs("<bignum>", stream
);
560 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
561 fputs("<real>", stream
);
563 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
564 fputs("<timedate>", stream
);
566 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
:
567 fputs("<blob>", stream
);
569 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
570 fputs("<uint32>", stream
);
572 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX
:
573 fputs("<complex>", stream
);
576 fprintf(stream
, "<format: %d>", (int)format
);
580 if (!attribute
->length
&& !attribute
->data
)
581 fputs("<NULL>", stream
);
585 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
586 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
588 print_uint32(stream
, *(UInt32
*) attribute
->data
);
592 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
594 int n
= attribute
->length
/ sizeof(UInt32
);
595 UInt32
* ptr
= (UInt32
*) attribute
->data
;
599 print_uint32(stream
, *ptr
++);
606 print_buffer(stream
, attribute
->length
, attribute
->data
);
616 fputs("data:\n", stream
);
617 print_buffer(stream
, length
, data
);
623 CSSM_DL_DB_HANDLE dldbHandle
= {};
624 const CSSM_DB_UNIQUE_RECORD
*uniqueRecordID
= NULL
;
626 status
= SecKeychainItemGetDLDBHandle(item
, &dldbHandle
);
629 sec_perror("SecKeychainItemGetDLDBHandle", status
);
634 status
= SecKeychainItemGetUniqueRecordID(item
, &uniqueRecordID
);
637 sec_perror("SecKeychainItemGetUniqueRecordID", status
);
642 status
= CSSM_DL_DataGetFromUniqueRecordId(dldbHandle
, uniqueRecordID
, NULL
, &data
);
645 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status
);
650 fputs("raw data:\n", stream
);
651 print_buffer(stream
, data
.Length
, data
.Data
);
654 /* @@@ Hmm which allocators should we use here? */
660 status
= SecKeychainItemCopyAccess(item
, &access
);
661 if (status
== errSecNoAccessForItem
)
662 fprintf(stream
, "no access control for this item\n");
667 sec_perror("SecKeychainItemCopyAccess", status
);
672 result
= print_access(stream
, access
, interactive
);
678 char buffer
[10] = {};
679 fprintf(stderr
, "Update access? ");
680 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
682 fprintf(stderr
, "Updating access\n");
683 status
= SecKeychainItemSetAccess(item
, access
);
686 sec_perror("SecKeychainItemSetAccess", status
);
701 status
= SecKeychainItemFreeAttributesAndData(attrList
, data
);
703 sec_perror("SecKeychainItemFreeAttributesAndData", status
);
708 status
= SecKeychainFreeAttributeInfo(info
);
710 sec_perror("SecKeychainFreeAttributeInfo", status
);
720 print_buffer_hex(FILE *stream
, UInt32 length
, const void *data
)
722 uint8
*p
= (uint8
*) data
;
726 fprintf(stream
, "%02X", ch
);
731 print_buffer_ascii(FILE *stream
, UInt32 length
, const void *data
)
733 uint8
*p
= (uint8
*) data
;
737 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
744 fputc('0' + ((ch
>> 6) & 7), stream
);
745 fputc('0' + ((ch
>> 3) & 7), stream
);
746 fputc('0' + ((ch
>> 0) & 7), stream
);
752 print_buffer(FILE *stream
, UInt32 length
, const void *data
)
754 uint8
*p
= (uint8
*) data
;
756 Boolean ascii
= FALSE
;
758 for (ix
= 0; ix
< length
; ++ix
)
761 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
771 print_buffer_hex(stream
, length
, data
);
779 print_buffer_ascii(stream
, length
, data
);
785 print_uint32(FILE *stream
, uint32 n
)
787 n
= OSSwapHostToBigInt32 (n
);
788 print_buffer(stream
, sizeof(UInt32
), &n
);
794 static const char digits
[] = "0123456789abcdef";
796 if ((p
= strchr(digits
, tolower(c
))))
803 fromHex(const char *hexDigits
, CSSM_DATA
*data
)
805 size_t bytes
= strlen(hexDigits
) / 2; // (discards malformed odd end)
806 if (bytes
> data
->Length
)
808 // length(bytes); // (will assert if we try to grow it)
810 for (n
= 0; n
< bytes
; n
++) {
811 data
->Data
[n
] = (uint8
)(hexValue(hexDigits
[2*n
]) << 4 | hexValue(hexDigits
[2*n
+1]));
816 safe_CFRelease(void *cfTypeRefPtr
)
818 CFTypeRef
*obj
= (CFTypeRef
*)cfTypeRefPtr
;
826 * map a 6-bit binary value to a printable character.
829 unsigned char bintoasc
[] =
830 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
833 * map 6 bits to a printing char
835 #define ENC(c) (bintoasc[((c) & 0x3f)])
840 * map one group of up to 3 bytes at inp to 4 bytes at outp.
841 * Count is number of valid bytes in *inp; if less than 3, the
842 * 1 or two extras must be zeros.
845 encChunk(const unsigned char *inp
,
849 unsigned char c1
, c2
, c3
, c4
;
852 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
853 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
871 static unsigned char *
872 malloc_enc64_with_lines(const unsigned char *inbuf
,
878 unsigned len
; // to malloc, liberal
879 unsigned olen
= 0; // actual output size
880 unsigned char *outbuf
;
881 unsigned char endbuf
[3];
887 outTextLen
= ((inlen
+ 2) / 3) * 4;
890 * linelen must be 0 mod 4 for this to work; round up...
892 if((linelen
& 0x03) != 0) {
893 linelen
= (linelen
+ 3) & 0xfffffffc;
895 numLines
= (outTextLen
+ linelen
- 1)/ linelen
;
902 * Total output size = encoded text size plus one newline per
903 * line of output, plus trailing NULL. We always generate newlines
904 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
906 len
= outTextLen
+ (2 * numLines
) + 1;
907 outbuf
= (unsigned char*)malloc(len
);
915 endbuf
[i
] = inbuf
[i
];
921 encChunk(endbuf
, outp
, inlen
);
925 encChunk(inbuf
, outp
, 3);
932 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
934 * last trailing newline added below
935 * Note we don't split 4-byte output chunks over newlines
949 print_buffer_pem(FILE *stream
, const char *headerString
, UInt32 length
, const void *data
)
955 fprintf(stream
, "-----BEGIN %s-----\n", headerString
);
956 buf
= malloc_enc64_with_lines(data
, length
, 64, &bufLen
);
957 fwrite(buf
, bufLen
, 1, stream
);
960 fprintf(stream
, "-----END %s-----\n", headerString
);