]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_utilities.c
Security-57336.1.9.tar.gz
[apple/security.git] / SecurityTool / keychain_utilities.c
1 /*
2 * Copyright (c) 2003-2009,2012,2014 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 License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * keychain_utilities.c
24 */
25
26 #include "keychain_utilities.h"
27 #include "security.h"
28
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>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/param.h>
37 #include <libkern/OSByteOrder.h>
38
39 #include "readline.h"
40
41 // SecTrustedApplicationValidateWithPath
42 #include <Security/SecTrustedApplicationPriv.h>
43
44
45 void check_obsolete_keychain(const char *kcName)
46 {
47 if(kcName == NULL) {
48 return;
49 }
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");
61 }
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");
71 }
72 }
73
74 SecKeychainRef
75 keychain_open(const char *name)
76 {
77 SecKeychainRef keychain = NULL;
78 OSStatus result;
79
80 check_obsolete_keychain(name);
81 if (name && name[0] != '/')
82 {
83 CFArrayRef dynamic = NULL;
84 result = SecKeychainCopyDomainSearchList(
85 kSecPreferencesDomainDynamic, &dynamic);
86 if (result)
87 {
88 sec_error("SecKeychainCopyDomainSearchList %s: %s",
89 name, sec_errstr(result));
90 return NULL;
91 }
92 else
93 {
94 uint32_t i;
95 uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0;
96
97 for (i = 0; i < count; ++i)
98 {
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);
104 if (result)
105 {
106 sec_error("SecKeychainGetPath %s: %s",
107 name, sec_errstr(result));
108 return NULL;
109 }
110 if (!strncmp(pathName, name, ioPathLength))
111 {
112 CFRetain(keychain);
113 CFRelease(dynamic);
114 return keychain;
115 }
116 }
117 CFRelease(dynamic);
118 }
119 }
120
121 result = SecKeychainOpen(name, &keychain);
122 if (result)
123 {
124 sec_error("SecKeychainOpen %s: %s", name, sec_errstr(result));
125 }
126
127 return keychain;
128 }
129
130 CFTypeRef
131 keychain_create_array(int argc, char * const *argv)
132 {
133 if (argc == 0)
134 return NULL;
135 else if (argc == 1)
136 return keychain_open(argv[0]);
137 else
138 {
139 CFMutableArrayRef keychains = CFArrayCreateMutable(NULL, argc, &kCFTypeArrayCallBacks);
140 int ix;
141 for (ix = 0; ix < argc; ++ix)
142 {
143 SecKeychainRef keychain = keychain_open(argv[ix]);
144 if (keychain)
145 {
146 CFArrayAppendValue(keychains, keychain);
147 CFRelease(keychain);
148 }
149 }
150
151 return keychains;
152 }
153 }
154
155 int
156 parse_fourcharcode(const char *name, UInt32 *code)
157 {
158 UInt32 cc = 0;
159 int len = (name) ? strlen(name) : 0;
160
161 // error check the name
162 if (len != 4)
163 {
164 fprintf(stderr, "Error: four-character types must be exactly 4 characters long.\n");
165 if (len == 3) {
166 fprintf(stderr, "(Try \"%s \" instead of \"%s\")\n", name, name);
167 }
168 return 1;
169 }
170
171 int i;
172 for (i = 0; i < 4; ++i)
173 {
174 cc = (cc << 8) | name[i];
175 }
176
177 *code = cc; // note: this is in host byte order, suitable for passing to APIs
178
179 return 0;
180 }
181
182 int
183 print_keychain_name(FILE *stream, SecKeychainRef keychain)
184 {
185 int result = 0;
186 char pathName[MAXPATHLEN];
187 UInt32 ioPathLength = sizeof(pathName);
188 OSStatus status = SecKeychainGetPath(keychain, &ioPathLength, pathName);
189 if (status)
190 {
191 sec_perror("SecKeychainGetPath", status);
192 result = 1;
193 goto loser;
194 }
195
196 print_buffer(stream, ioPathLength, pathName);
197
198 loser:
199 return result;
200 }
201
202 static void
203 print_cfdata(FILE *stream, CFDataRef data)
204 {
205 if (data)
206 return print_buffer(stream, CFDataGetLength(data), CFDataGetBytePtr(data));
207 else
208 fprintf(stream, "<NULL>");
209 }
210
211 static void
212 print_cfstring(FILE *stream, CFStringRef string)
213 {
214 if (!string)
215 fprintf(stream, "<NULL>");
216 else
217 {
218 const char *utf8 = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
219 if (utf8)
220 fprintf(stream, "%s", utf8);
221 else
222 {
223 CFRange rangeToProcess = CFRangeMake(0, CFStringGetLength(string));
224 while (rangeToProcess.length > 0)
225 {
226 UInt8 localBuffer[256];
227 CFIndex usedBufferLength;
228 CFIndex numChars = CFStringGetBytes(string, rangeToProcess,
229 kCFStringEncodingUTF8, '?', FALSE, localBuffer,
230 sizeof(localBuffer), &usedBufferLength);
231 if (numChars == 0)
232 break; // Failed to convert anything...
233
234 fprintf(stream, "%.*s", (int)usedBufferLength, localBuffer);
235 rangeToProcess.location += numChars;
236 rangeToProcess.length -= numChars;
237 }
238 }
239 }
240 }
241
242 static int
243 print_access(FILE *stream, SecAccessRef access, Boolean interactive)
244 {
245 CFArrayRef aclList = NULL;
246 CFIndex aclix, aclCount;
247 int result = 0;
248 OSStatus status;
249
250 status = SecAccessCopyACLList(access, &aclList);
251 if (status)
252 {
253 sec_perror("SecAccessCopyACLList", status);
254 result = 1;
255 goto loser;
256 }
257
258 aclCount = CFArrayGetCount(aclList);
259 fprintf(stream, "access: %lu entries\n", aclCount);
260 for (aclix = 0; aclix < aclCount; ++aclix)
261 {
262 CFArrayRef applicationList = NULL;
263 CFStringRef description = NULL;
264 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {};
265 CFIndex appix, appCount;
266
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);
271 if (status)
272 {
273 sec_perror("SecACLGetAuthorizations", status);
274 result = 1;
275 goto loser;
276 }
277
278 fprintf(stream, " entry %lu:\n authorizations (%lu):", aclix,
279 (unsigned long)tagCount);
280 for (tagix = 0; tagix < tagCount; ++tagix)
281 {
282 CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix];
283 switch (tag)
284 {
285 case CSSM_ACL_AUTHORIZATION_ANY:
286 fputs(" any", stream);
287 break;
288 case CSSM_ACL_AUTHORIZATION_LOGIN:
289 fputs(" login", stream);
290 break;
291 case CSSM_ACL_AUTHORIZATION_GENKEY:
292 fputs(" genkey", stream);
293 break;
294 case CSSM_ACL_AUTHORIZATION_DELETE:
295 fputs(" delete", stream);
296 break;
297 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED:
298 fputs(" export_wrapped", stream);
299 break;
300 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR:
301 fputs(" export_clear", stream);
302 break;
303 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED:
304 fputs(" import_wrapped", stream);
305 break;
306 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR:
307 fputs(" import_clear", stream);
308 break;
309 case CSSM_ACL_AUTHORIZATION_SIGN:
310 fputs(" sign", stream);
311 break;
312 case CSSM_ACL_AUTHORIZATION_ENCRYPT:
313 fputs(" encrypt", stream);
314 break;
315 case CSSM_ACL_AUTHORIZATION_DECRYPT:
316 fputs(" decrypt", stream);
317 break;
318 case CSSM_ACL_AUTHORIZATION_MAC:
319 fputs(" mac", stream);
320 break;
321 case CSSM_ACL_AUTHORIZATION_DERIVE:
322 fputs(" derive", stream);
323 break;
324 case CSSM_ACL_AUTHORIZATION_DBS_CREATE:
325 fputs(" dbs_create", stream);
326 break;
327 case CSSM_ACL_AUTHORIZATION_DBS_DELETE:
328 fputs(" dbs_delete", stream);
329 break;
330 case CSSM_ACL_AUTHORIZATION_DB_READ:
331 fputs(" db_read", stream);
332 break;
333 case CSSM_ACL_AUTHORIZATION_DB_INSERT:
334 fputs(" db_insert", stream);
335 break;
336 case CSSM_ACL_AUTHORIZATION_DB_MODIFY:
337 fputs(" db_modify", stream);
338 break;
339 case CSSM_ACL_AUTHORIZATION_DB_DELETE:
340 fputs(" db_delete", stream);
341 break;
342 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL:
343 fputs(" change_acl", stream);
344 break;
345 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER:
346 fputs(" change_owner", stream);
347 break;
348 default:
349 fprintf(stream, " tag=%lu", (unsigned long)tag);
350 break;
351 }
352 }
353 fputc('\n', stream);
354
355 status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector);
356 if (status)
357 {
358 sec_perror("SecACLCopySimpleContents", status);
359 continue;
360 }
361
362 if (promptSelector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE)
363 fputs(" require-password\n", stream);
364 else
365 fputs(" don't-require-password\n", stream);
366
367 fputs(" description: ", stream);
368 print_cfstring(stream, description);
369 fputc('\n', stream);
370
371 if (applicationList)
372 {
373 appCount = CFArrayGetCount(applicationList);
374 fprintf(stream, " applications (%lu):\n", appCount);
375 }
376 else
377 {
378 appCount = 0;
379 fprintf(stream, " applications: <null>\n");
380 }
381
382 for (appix = 0; appix < appCount; ++appix)
383 {
384 const UInt8* bytes;
385 SecTrustedApplicationRef app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(applicationList, appix);
386 CFDataRef data = NULL;
387 fprintf(stream, " %lu: ", appix);
388 status = SecTrustedApplicationCopyData(app, &data);
389 if (status)
390 {
391 sec_perror("SecTrustedApplicationCopyData", status);
392 continue;
393 }
394
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)");
400 } else {
401 fprintf(stream, " (status %d)", (int)status);
402 }
403 fprintf(stream, "\n");
404 } else {
405 print_cfdata(stream, data);
406 fputc('\n', stream);
407 }
408 if (data)
409 CFRelease(data);
410 }
411
412 if (applicationList)
413 CFRelease(applicationList);
414
415 if (description)
416 CFRelease(description);
417
418 if (interactive)
419 {
420 char buffer[10] = {};
421 fprintf(stderr, "Remove this acl? ");
422 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
423 {
424 fprintf(stderr, "removing acl\n");
425 status = SecACLRemove(acl);
426 if (status)
427 {
428 sec_perror("SecACLRemove", status);
429 continue;
430 }
431 }
432 }
433 }
434
435 loser:
436 if (aclList)
437 CFRelease(aclList);
438
439 return result;
440 }
441
442 int
443 print_keychain_item_attributes(FILE *stream, SecKeychainItemRef item, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
444 {
445 int result = 0;
446 unsigned int ix;
447 OSStatus status;
448 SecKeychainRef keychain = NULL;
449 SecAccessRef access = NULL;
450 SecItemClass itemClass = 0;
451 UInt32 itemID;
452 SecKeychainAttributeList *attrList = NULL;
453 SecKeychainAttributeInfo *info = NULL;
454 UInt32 length = 0;
455 void *data = NULL;
456
457 status = SecKeychainItemCopyKeychain(item, &keychain);
458 if (status)
459 {
460 sec_perror("SecKeychainItemCopyKeychain", status);
461 result = 1;
462 goto loser;
463 }
464
465 fputs("keychain: ", stream);
466 result = print_keychain_name(stream, keychain);
467 fputc('\n', stream);
468 if (result)
469 goto loser;
470
471 /* First find out the item class. */
472 status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
473 if (status)
474 {
475 sec_perror("SecKeychainItemCopyAttributesAndData", status);
476 result = 1;
477 goto loser;
478 }
479
480 fputs("class: ", stream);
481 char buffer[4];
482 buffer[3] = itemClass & 0xFF;
483 buffer[2] = (itemClass >> 8) & 0xFF;
484 buffer[1] = (itemClass >> 16) & 0xFF;
485 buffer[0] = (itemClass >> 24) & 0xFF;
486
487 print_buffer(stream, 4, buffer);
488 fputs("\nattributes:\n", stream);
489
490 switch (itemClass)
491 {
492 case kSecInternetPasswordItemClass:
493 itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
494 break;
495 case kSecGenericPasswordItemClass:
496 itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
497 break;
498 case 'ashp': /* kSecAppleSharePasswordItemClass */
499 itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
500 break;
501 default:
502 itemID = itemClass;
503 break;
504 }
505
506 /* Now get the AttributeInfo for it. */
507 status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
508 if (status)
509 {
510 sec_perror("SecKeychainAttributeInfoForItemID", status);
511 result = 1;
512 goto loser;
513 }
514
515 status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList,
516 show_data ? &length : NULL,
517 show_data ? &data : NULL);
518 if (status)
519 {
520 sec_perror("SecKeychainItemCopyAttributesAndData", status);
521 result = 1;
522 goto loser;
523 }
524
525 if (info->count != attrList->count)
526 {
527 sec_error("info count: %ld != attribute count: %ld", info->count, attrList->count);
528 result = 1;
529 goto loser;
530 }
531
532 for (ix = 0; ix < info->count; ++ix)
533 {
534 UInt32 tag = info->tag[ix];
535 UInt32 format = info->format[ix];
536 SecKeychainAttribute *attribute = &attrList->attr[ix];
537 if (tag != attribute->tag)
538 {
539 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix, info->count, tag, attribute->tag);
540 result = 1;
541 goto loser;
542 }
543
544 fputs(" ", stream);
545 print_uint32(stream, tag);
546 switch (format)
547 {
548 case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
549 fputs("<string>", stream);
550 break;
551 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
552 fputs("<sint32>", stream);
553 break;
554 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
555 fputs("<uint32>", stream);
556 break;
557 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM:
558 fputs("<bignum>", stream);
559 break;
560 case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
561 fputs("<real>", stream);
562 break;
563 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
564 fputs("<timedate>", stream);
565 break;
566 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
567 fputs("<blob>", stream);
568 break;
569 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
570 fputs("<uint32>", stream);
571 break;
572 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX:
573 fputs("<complex>", stream);
574 break;
575 default:
576 fprintf(stream, "<format: %d>", (int)format);
577 break;
578 }
579 fputs("=", stream);
580 if (!attribute->length && !attribute->data)
581 fputs("<NULL>", stream);
582 else
583 { switch (format)
584 {
585 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
586 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
587 {
588 print_uint32(stream, *(UInt32*) attribute->data);
589 break;
590 }
591
592 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
593 {
594 int n = attribute->length / sizeof(UInt32);
595 UInt32* ptr = (UInt32*) attribute->data;
596
597 while (n--)
598 {
599 print_uint32(stream, *ptr++);
600 }
601 }
602 break;
603
604 default:
605 {
606 print_buffer(stream, attribute->length, attribute->data);
607 }
608 break;
609 }
610 }
611 fputc('\n', stream);
612 }
613
614 if (show_data)
615 {
616 fputs("data:\n", stream);
617 print_buffer(stream, length, data);
618 fputc('\n', stream);
619 }
620
621 if (show_raw_data)
622 {
623 CSSM_DL_DB_HANDLE dldbHandle = {};
624 const CSSM_DB_UNIQUE_RECORD *uniqueRecordID = NULL;
625 CSSM_DATA data = {};
626 status = SecKeychainItemGetDLDBHandle(item, &dldbHandle);
627 if (status)
628 {
629 sec_perror("SecKeychainItemGetDLDBHandle", status);
630 result = 1;
631 goto loser;
632 }
633
634 status = SecKeychainItemGetUniqueRecordID(item, &uniqueRecordID);
635 if (status)
636 {
637 sec_perror("SecKeychainItemGetUniqueRecordID", status);
638 result = 1;
639 goto loser;
640 }
641
642 status = CSSM_DL_DataGetFromUniqueRecordId(dldbHandle, uniqueRecordID, NULL, &data);
643 if (status)
644 {
645 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status);
646 result = 1;
647 goto loser;
648 }
649
650 fputs("raw data:\n", stream);
651 print_buffer(stream, data.Length, data.Data);
652 fputc('\n', stream);
653
654 /* @@@ Hmm which allocators should we use here? */
655 free(data.Data);
656 }
657
658 if (show_acl)
659 {
660 status = SecKeychainItemCopyAccess(item, &access);
661 if (status == errSecNoAccessForItem)
662 fprintf(stream, "no access control for this item\n");
663 else
664 {
665 if (status)
666 {
667 sec_perror("SecKeychainItemCopyAccess", status);
668 result = 1;
669 goto loser;
670 }
671
672 result = print_access(stream, access, interactive);
673 if (result)
674 goto loser;
675
676 if (interactive)
677 {
678 char buffer[10] = {};
679 fprintf(stderr, "Update access? ");
680 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
681 {
682 fprintf(stderr, "Updating access\n");
683 status = SecKeychainItemSetAccess(item, access);
684 if (status)
685 {
686 sec_perror("SecKeychainItemSetAccess", status);
687 result = 1;
688 goto loser;
689 }
690 }
691 }
692 }
693 }
694
695 loser:
696 if (access)
697 CFRelease(access);
698
699 if (attrList)
700 {
701 status = SecKeychainItemFreeAttributesAndData(attrList, data);
702 if (status)
703 sec_perror("SecKeychainItemFreeAttributesAndData", status);
704 }
705
706 if (info)
707 {
708 status = SecKeychainFreeAttributeInfo(info);
709 if (status)
710 sec_perror("SecKeychainFreeAttributeInfo", status);
711 }
712
713 if (keychain)
714 CFRelease(keychain);
715
716 return result;
717 }
718
719 static void
720 print_buffer_hex(FILE *stream, UInt32 length, const void *data)
721 {
722 uint8 *p = (uint8 *) data;
723 while (length--)
724 {
725 int ch = *p++;
726 fprintf(stream, "%02X", ch);
727 }
728 }
729
730 static void
731 print_buffer_ascii(FILE *stream, UInt32 length, const void *data)
732 {
733 uint8 *p = (uint8 *) data;
734 while (length--)
735 {
736 int ch = *p++;
737 if (ch >= ' ' && ch <= '~' && ch != '\\')
738 {
739 fputc(ch, stream);
740 }
741 else
742 {
743 fputc('\\', stream);
744 fputc('0' + ((ch >> 6) & 7), stream);
745 fputc('0' + ((ch >> 3) & 7), stream);
746 fputc('0' + ((ch >> 0) & 7), stream);
747 }
748 }
749 }
750
751 void
752 print_buffer(FILE *stream, UInt32 length, const void *data)
753 {
754 uint8 *p = (uint8 *) data;
755 Boolean hex = FALSE;
756 Boolean ascii = FALSE;
757 UInt32 ix;
758 for (ix = 0; ix < length; ++ix)
759 {
760 int ch = *p++;
761 if (ch >= ' ' && ch <= '~' && ch != '\\')
762 ascii = TRUE;
763 else
764 hex = TRUE;
765 }
766
767 if (hex)
768 {
769 fputc('0', stream);
770 fputc('x', stream);
771 print_buffer_hex(stream, length, data);
772 if (ascii)
773 fputc(' ', stream);
774 fputc(' ', stream);
775 }
776 if (ascii)
777 {
778 fputc('"', stream);
779 print_buffer_ascii(stream, length, data);
780 fputc('"', stream);
781 }
782 }
783
784 void
785 print_uint32(FILE *stream, uint32 n)
786 {
787 n = OSSwapHostToBigInt32 (n);
788 print_buffer(stream, sizeof(UInt32), &n);
789 }
790
791 unsigned char
792 hexValue(char c)
793 {
794 static const char digits[] = "0123456789abcdef";
795 char *p;
796 if ((p = strchr(digits, tolower(c))))
797 return p - digits;
798 else
799 return 0;
800 }
801
802 void
803 fromHex(const char *hexDigits, CSSM_DATA *data)
804 {
805 size_t bytes = strlen(hexDigits) / 2; // (discards malformed odd end)
806 if (bytes > data->Length)
807 return;
808 // length(bytes); // (will assert if we try to grow it)
809 size_t n;
810 for (n = 0; n < bytes; n++) {
811 data->Data[n] = (uint8)(hexValue(hexDigits[2*n]) << 4 | hexValue(hexDigits[2*n+1]));
812 }
813 }
814
815 void
816 safe_CFRelease(void *cfTypeRefPtr)
817 {
818 CFTypeRef *obj = (CFTypeRef *)cfTypeRefPtr;
819 if (obj && *obj) {
820 CFRelease(*obj);
821 *obj = NULL;
822 }
823 }
824
825 /*
826 * map a 6-bit binary value to a printable character.
827 */
828 static const
829 unsigned char bintoasc[] =
830 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
831
832 /*
833 * map 6 bits to a printing char
834 */
835 #define ENC(c) (bintoasc[((c) & 0x3f)])
836
837 #define PAD '='
838
839 /*
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.
843 */
844 static void
845 encChunk(const unsigned char *inp,
846 unsigned char *outp,
847 int count)
848 {
849 unsigned char c1, c2, c3, c4;
850
851 c1 = *inp >> 2;
852 c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf);
853 c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3);
854 c4 = inp[2] & 0x3f;
855 *outp++ = ENC(c1);
856 *outp++ = ENC(c2);
857 if (count == 1) {
858 *outp++ = PAD;
859 *outp = PAD;
860 } else {
861 *outp++ = ENC(c3);
862 if (count == 2) {
863 *outp = PAD;
864 }
865 else {
866 *outp = ENC(c4);
867 }
868 }
869 }
870
871 static unsigned char *
872 malloc_enc64_with_lines(const unsigned char *inbuf,
873 unsigned inlen,
874 unsigned linelen,
875 unsigned *outlen)
876 {
877 unsigned outTextLen;
878 unsigned len; // to malloc, liberal
879 unsigned olen = 0; // actual output size
880 unsigned char *outbuf;
881 unsigned char endbuf[3];
882 unsigned i;
883 unsigned char *outp;
884 unsigned numLines;
885 unsigned thisLine;
886
887 outTextLen = ((inlen + 2) / 3) * 4;
888 if(linelen) {
889 /*
890 * linelen must be 0 mod 4 for this to work; round up...
891 */
892 if((linelen & 0x03) != 0) {
893 linelen = (linelen + 3) & 0xfffffffc;
894 }
895 numLines = (outTextLen + linelen - 1)/ linelen;
896 }
897 else {
898 numLines = 1;
899 }
900
901 /*
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.
905 */
906 len = outTextLen + (2 * numLines) + 1;
907 outbuf = (unsigned char*)malloc(len);
908 outp = outbuf;
909 thisLine = 0;
910
911 while(inlen) {
912 if(inlen < 3) {
913 for(i=0; i<3; i++) {
914 if(i < inlen) {
915 endbuf[i] = inbuf[i];
916 }
917 else {
918 endbuf[i] = 0;
919 }
920 }
921 encChunk(endbuf, outp, inlen);
922 inlen = 0;
923 }
924 else {
925 encChunk(inbuf, outp, 3);
926 inlen -= 3;
927 inbuf += 3;
928 }
929 outp += 4;
930 thisLine += 4;
931 olen += 4;
932 if((linelen != 0) && (thisLine >= linelen) && inlen) {
933 /*
934 * last trailing newline added below
935 * Note we don't split 4-byte output chunks over newlines
936 */
937 *outp++ = '\n';
938 olen++;
939 thisLine = 0;
940 }
941 }
942 *outp++ = '\n';
943 olen += 1;
944 *outlen = olen;
945 return outbuf;
946 }
947
948 void
949 print_buffer_pem(FILE *stream, const char *headerString, UInt32 length, const void *data)
950 {
951 unsigned char *buf;
952 unsigned bufLen;
953
954 if (headerString)
955 fprintf(stream, "-----BEGIN %s-----\n", headerString);
956 buf = malloc_enc64_with_lines(data, length, 64, &bufLen);
957 fwrite(buf, bufLen, 1, stream);
958 free(buf);
959 if (headerString)
960 fprintf(stream, "-----END %s-----\n", headerString);
961 }