1/*
2 * rootUtils.cpp - utility routines for rootStoreTool
3 */
4
5#include <stdlib.h>
6#include <strings.h>
7#include <stdio.h>
8#include <unistd.h>
9#include "rootUtils.h"
10#include <Security/SecCertificatePriv.h>
11#include <Security/SecBasePriv.h>
12#include <Security/SecTrustSettings.h>
13#include <Security/TrustSettingsSchema.h>		/* private header */
14#include <Security/SecAsn1Coder.h>
15#include <Security/nameTemplates.h>				/* oh frabjous day */
16
17#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
18
19static int indentSize = 0;
20void indentIncr(void)	{ indentSize += 3; }
21void indentDecr(void)	{ indentSize -= 3; }
22
23void indent(void)
24{
25	if(indentSize < 0) {
26		printf("***indent screwup\n");
27		indentSize = 0;
28	}
29	for (int dex=0; dex<indentSize; dex++) {
30		putchar(' ');
31	}
32}
33
34void printAscii(
35	const char *buf,
36	unsigned len,
37	unsigned maxLen)
38{
39	bool doEllipsis = false;
40	if(len > maxLen) {
41		len = maxLen;
42		doEllipsis = true;
43	}
44	for(unsigned dex=0; dex<len; dex++) {
45		char c = *buf++;
46		if(isalnum(c) || (c == ' ')) {
47			putchar(c);
48		}
49		else {
50			putchar('.');
51		}
52		fflush(stdout);
53	}
54	if(doEllipsis) {
55		printf("...etc.");
56	}
57}
58
59void printHex(
60	const unsigned char *buf,
61	unsigned len,
62	unsigned maxLen)
63{
64	bool doEllipsis = false;
65	if(len > maxLen) {
66		len = maxLen;
67		doEllipsis = true;
68	}
69	for(unsigned dex=0; dex<len; dex++) {
70		printf("%02X ", *buf++);
71	}
72	if(doEllipsis) {
73		printf("...etc.");
74	}
75}
76
77void printOid(
78	const void *buf,
79	unsigned len,
80	OidParser &parser)
81{
82	char outstr[OID_PARSER_STRING_SIZE];
83	parser.oidParse((const unsigned char *)buf, len, outstr);
84	printf("%s", outstr);
85}
86
87void printData(
88	const char *label,
89	CFDataRef data,
90	PrintDataType whichType,
91	OidParser &parser)
92{
93	const unsigned char *buf = CFDataGetBytePtr(data);
94	unsigned len = CFDataGetLength(data);
95
96	printf("%s: ", label);
97	switch(whichType) {
98		case PD_Hex:
99			printHex(buf, len, 16);
100			break;
101		case PD_ASCII:
102			printAscii((const char *)buf, len, 50);
103			break;
104		case PD_OID:
105			printOid(buf, len, parser);
106	}
107	putchar('\n');
108}
109
110/* print the contents of a CFString */
111void printCfStr(
112	CFStringRef cfstr)
113{
114	CFDataRef strData = CFStringCreateExternalRepresentation(NULL, cfstr,
115		kCFStringEncodingUTF8, true);
116	if(strData == NULL) {
117		printf("<<string decode error>>");
118		return;
119	}
120	const char *cp = (const char *)CFDataGetBytePtr(strData);
121	CFIndex len = CFDataGetLength(strData);
122	for(CFIndex dex=0; dex<len; dex++) {
123		putchar(*cp++);
124	}
125	CFRelease(strData);
126}
127
128/* print a CFDateRef */
129static const char *months[12] = {
130	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
131	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
132};
133
134void printCFDate(
135	CFDateRef dateRef)
136{
137	CFAbsoluteTime absTime = CFDateGetAbsoluteTime(dateRef);
138	if(absTime == 0.0) {
139		printf("<<Malformed CFDateeRef>>\n");
140		return;
141	}
142	CFGregorianDate gregDate = CFAbsoluteTimeGetGregorianDate(absTime, NULL);
143	const char *month = "Unknown";
144	if((gregDate.month > 12) || (gregDate.month <= 0)) {
145		printf("Huh? GregDate.month > 11. These amps only GO to 11.\n");
146	}
147	else {
148		month = months[gregDate.month - 1];
149	}
150	printf("%s %d, %ld %02d:%02d",
151		month, gregDate.day, gregDate.year, gregDate.hour, gregDate.minute);
152}
153
154/* print a CFNumber */
155void printCfNumber(
156	CFNumberRef cfNum)
157{
158	SInt32 s;
159	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &s)) {
160		printf("***CFNumber overflow***");
161		return;
162	}
163	printf("%ld", s);
164}
165
166/* print a CFNumber as a SecTrustSettingsResult */
167void printResult(
168	CFNumberRef cfNum)
169{
170	SInt32 n;
171	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &n)) {
172		printf("***CFNumber overflow***");
173		return;
174	}
175	const char *s;
176	char bogus[100];
177	switch(n) {
178		case kSecTrustSettingsResultInvalid: s = "kSecTrustSettingsResultInvalid"; break;
179		case kSecTrustSettingsResultTrustRoot: s = "kSecTrustSettingsResultTrustRoot"; break;
180		case kSecTrustSettingsResultTrustAsRoot: s = "kSecTrustSettingsResultTrustAsRoot"; break;
181		case kSecTrustSettingsResultDeny: s = "kSecTrustSettingsResultDeny"; break;
182		case kSecTrustSettingsResultUnspecified:    s = "kSecTrustSettingsResultUnspecified"; break;
183		default:
184			sprintf(bogus, "Unknown SecTrustSettingsResult (%ld)", n);
185			s = bogus;
186			break;
187	}
188	printf("%s", s);
189}
190
191/* print a CFNumber as SecTrustSettingsKeyUsage */
192void printKeyUsage(
193	CFNumberRef cfNum)
194{
195	SInt32 s;
196	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &s)) {
197		printf("***CFNumber overflow***");
198		return;
199	}
200	uint32 n = (uint32)s;
201	if(n == kSecTrustSettingsKeyUseAny) {
202		printf("<any>");
203		return;
204	}
205	else if(n == 0) {
206		printf("<none>");
207		return;
208	}
209	printf("< ");
210	if(n & kSecTrustSettingsKeyUseSignature) {
211		printf("Signature ");
212	}
213	if(n & kSecTrustSettingsKeyUseEnDecryptData) {
214		printf("EnDecryptData ");
215	}
216	if(n & kSecTrustSettingsKeyUseEnDecryptKey) {
217		printf("EnDecryptKey ");
218	}
219	if(n & kSecTrustSettingsKeyUseSignCert) {
220		printf("SignCert ");
221	}
222	if(n & kSecTrustSettingsKeyUseSignRevocation) {
223		printf("SignRevocation ");
224	}
225	if(n & kSecTrustSettingsKeyUseKeyExchange) {
226		printf("KeyExchange ");
227	}
228	printf(" >");
229}
230
231/* print a CFNumber as CSSM_RETURN string */
232void printCssmErr(
233	CFNumberRef cfNum)
234{
235	SInt32 s;
236	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &s)) {
237		printf("***CFNumber overflow***");
238		return;
239	}
240	printf("%s", cssmErrorString((CSSM_RETURN)s));
241}
242
243/* print cert's label (the one SecCertificate infers) */
244OSStatus printCertLabel(
245	SecCertificateRef certRef)
246{
247	OSStatus ortn;
248	CFStringRef label;
249
250	ortn = SecCertificateInferLabel(certRef, &label);
251	if(ortn) {
252		cssmPerror("SecCertificateInferLabel", ortn);
253		return ortn;
254	}
255	printCfStr(label);
256	CFRelease(label);
257	return noErr;
258}
259
260/*
261 * How many items in a NULL-terminated array of pointers?
262 */
263static unsigned nssArraySize(
264	const void **array)
265{
266    unsigned count = 0;
267    if (array) {
268		while (*array++) {
269			count++;
270		}
271    }
272    return count;
273}
274
275static int compareOids(
276	const CSSM_OID *data1,
277	const CSSM_OID *data2)
278{
279	if((data1 == NULL) || (data1->Data == NULL) ||
280	   (data2 == NULL) || (data2->Data == NULL) ||
281	   (data1->Length != data2->Length)) {
282		return 0;
283	}
284	if(data1->Length != data2->Length) {
285		return 0;
286	}
287	return memcmp(data1->Data, data2->Data, data1->Length) == 0;
288}
289
290static void printRdn(const NSS_RDN *rdn, OidParser &parser)
291{
292	unsigned numAtvs = nssArraySize((const void **)rdn->atvs);
293	char						*fieldName;
294
295	for(unsigned dex=0; dex<numAtvs; dex++) {
296		const NSS_ATV *atv = rdn->atvs[dex];
297		if(compareOids(&atv->type, &CSSMOID_CountryName)) {
298			fieldName = "Country       ";
299		}
300		else if(compareOids(&atv->type, &CSSMOID_OrganizationName)) {
301			fieldName = "Org           ";
302		}
303		else if(compareOids(&atv->type, &CSSMOID_LocalityName)) {
304			fieldName = "Locality      ";
305		}
306		else if(compareOids(&atv->type, &CSSMOID_OrganizationalUnitName)) {
307			fieldName = "OrgUnit       ";
308		}
309		else if(compareOids(&atv->type, &CSSMOID_CommonName)) {
310			fieldName = "Common Name   ";
311		}
312		else if(compareOids(&atv->type, &CSSMOID_Surname)) {
313			fieldName = "Surname       ";
314		}
315		else if(compareOids(&atv->type, &CSSMOID_Title)) {
316			fieldName = "Title         ";
317		}
318		else if(compareOids(&atv->type, &CSSMOID_Surname)) {
319			fieldName = "Surname       ";
320		}
321		else if(compareOids(&atv->type, &CSSMOID_StateProvinceName)) {
322			fieldName = "State         ";
323		}
324		else if(compareOids(&atv->type, &CSSMOID_CollectiveStateProvinceName)) {
325			fieldName = "Coll. State   ";
326		}
327		else if(compareOids(&atv->type, &CSSMOID_EmailAddress)) {
328			/* deprecated, used by Thawte */
329			fieldName = "Email addrs   ";
330		}
331		else {
332			fieldName = "Other name    ";
333		}
334		indent(); printf("%s      : ", fieldName);
335		/* Not strictly true here, but we'll just assume we can print everything */
336		printAscii((char *)atv->value.item.Data, atv->value.item.Length,
337			atv->value.item.Length);
338		putchar('\n');
339	}
340}
341
342/* print a CFData as an X509 Name (i.e., subject or issuer) */
343void printCfName(
344	CFDataRef nameData,
345	OidParser &parser)
346{
347	SecAsn1CoderRef coder = NULL;
348	OSStatus ortn;
349
350	ortn = SecAsn1CoderCreate(&coder);
351	if(ortn) {
352		cssmPerror("SecAsn1CoderCreate", ortn);
353		return;
354	}
355	/* subsequent errors to errOut: */
356
357	NSS_Name nssName = {NULL};
358	unsigned numRdns;
359
360	ortn = SecAsn1Decode(coder,
361		CFDataGetBytePtr(nameData), CFDataGetLength(nameData),
362		kSecAsn1NameTemplate,
363		&nssName);
364	if(ortn) {
365		printf("***Error decoding NSS_Name\n");
366		goto errOut;
367	}
368	numRdns = nssArraySize((const void **)nssName.rdns);
369	for(unsigned dex=0; dex<numRdns; dex++) {
370		printRdn(nssName.rdns[dex], parser);
371	}
372
373errOut:
374	if(coder) {
375		SecAsn1CoderRelease(coder);
376	}
377}
378
379