1/*
2 * Copyright (c) 2003-2009 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
45void 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
74SecKeychainRef
75keychain_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
130CFTypeRef
131keychain_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
155int
156parse_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
182int
183print_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
198loser:
199	return result;
200}
201
202static void
203print_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
211static void
212print_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
242static int
243print_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
435loser:
436	if (aclList)
437		CFRelease(aclList);
438
439	return result;
440}
441
442int
443print_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
695loser:
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
719static void
720print_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
730static void
731print_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
751void
752print_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
784void
785print_uint32(FILE *stream, uint32 n)
786{
787	n = OSSwapHostToBigInt32 (n);
788	print_buffer(stream, sizeof(UInt32), &n);
789}
790
791unsigned char
792hexValue(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
802void
803fromHex(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
815void
816safe_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 */
828static const
829unsigned 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 */
844static void
845encChunk(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
871static unsigned char *
872malloc_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
948void
949print_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}
962