1/*
2 * Copyright (c) 2002,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18/*
19 * cuPrintCert.cpp - Parse a cert or CRL, dump contents.
20 */
21#include "cuCdsaUtils.h"
22#include <stdio.h>
23#include <stdlib.h>
24#include <Security/oidscert.h>
25#include <Security/oidscrl.h>
26#include <Security/x509defs.h>
27#include <Security/oidsattr.h>
28#include <Security/oidsalg.h>
29#include <Security/cssmapple.h>
30#include <string.h>
31#include "cuPrintCert.h"
32#include "cuOidParser.h"
33#include "cuTimeStr.h"
34#include <Security/certextensions.h>
35#include <Security/SecAsn1Coder.h>
36#include <Security/keyTemplates.h>
37
38static const char *months[] = {
39	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
40	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
41};
42
43static void printTimeStr(const CSSM_DATA *cssmTime)
44{
45	struct tm tm;
46
47	/* ignore cssmTime->timeType for now */
48	if(cuTimeStringToTm((char *)cssmTime->Data, (unsigned int)cssmTime->Length, &tm)) {
49		printf("***Bad time string format***\n");
50		return;
51	}
52	if(tm.tm_mon > 11) {
53		printf("***Bad time string format***\n");
54		return;
55	}
56	printf("%02d:%02d:%02d %s %d, %04d\n",
57		tm.tm_hour, tm.tm_min, tm.tm_sec,
58		months[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
59
60}
61
62
63static void printTime(const CSSM_X509_TIME *cssmTime)
64{
65	/* ignore cssmTime->timeType for now */
66	printTimeStr(&cssmTime->time);
67}
68
69static void printDataAsHex(
70	const CSSM_DATA *d,
71	unsigned maxToPrint = 0)		// optional, 0 means print it all
72{
73	unsigned i;
74	bool more = false;
75	uint32 len = (uint32)d->Length;
76	uint8 *cp = d->Data;
77
78	if((maxToPrint != 0) && (len > maxToPrint)) {
79		len = maxToPrint;
80		more = true;
81	}
82	for(i=0; i<len; i++) {
83		printf("%02X ", ((unsigned char *)cp)[i]);
84	}
85	if(more) {
86		printf("...\n");
87	}
88	else {
89		printf("\n");
90	}
91}
92
93/*
94 * Identify CSSM_BER_TAG with a C string.
95 */
96static const char *tagTypeString(
97	CSSM_BER_TAG tagType)
98{
99	static char unknownType[80];
100
101	switch(tagType) {
102		case BER_TAG_UNKNOWN:
103			return "BER_TAG_UNKNOWN";
104		case BER_TAG_BOOLEAN:
105			return "BER_TAG_BOOLEAN";
106		case BER_TAG_INTEGER:
107			return "BER_TAG_INTEGER";
108		case BER_TAG_BIT_STRING:
109			return "BER_TAG_BIT_STRING";
110		case BER_TAG_OCTET_STRING:
111			return "BER_TAG_OCTET_STRING";
112		case BER_TAG_NULL:
113			return "BER_TAG_NULL";
114		case BER_TAG_OID:
115			return "BER_TAG_OID";
116		case BER_TAG_SEQUENCE:
117			return "BER_TAG_SEQUENCE";
118		case BER_TAG_SET:
119			return "BER_TAG_SET";
120		case BER_TAG_PRINTABLE_STRING:
121			return "BER_TAG_PRINTABLE_STRING";
122		case BER_TAG_T61_STRING:
123			return "BER_TAG_T61_STRING";
124		case BER_TAG_IA5_STRING:
125			return "BER_TAG_IA5_STRING";
126		case BER_TAG_UTC_TIME:
127			return "BER_TAG_UTC_TIME";
128		case BER_TAG_GENERALIZED_TIME:
129			return "BER_TAG_GENERALIZED_TIME";
130		default:
131			sprintf(unknownType, "Other type (0x%x)", tagType);
132			return unknownType;
133	}
134}
135
136/*
137 * Print an OID, assumed to be in BER encoded "Intel" format
138 * Length is inferred from oid->Length
139 * Tag is implied
140 */
141static void printOid(OidParser &parser, const CSSM_DATA *oid)
142{
143	char strBuf[OID_PARSER_STRING_SIZE];
144
145	if(oid == NULL) {
146		printf("NULL\n");
147		return;
148	}
149	if((oid->Length == 0) || (oid->Data == NULL)) {
150		printf("EMPTY\n");
151		return;
152	}
153	parser.oidParse(oid->Data, (unsigned int)oid->Length, strBuf);
154	printf("%s\n", strBuf);
155}
156
157/*
158 * Used to print generic blobs which we don't really understand.
159 * The bytesToPrint argument is usually thing->Length; it's here because snacc
160 * peports lengths of bit strings in BITS. Caller knows this and
161 * modifies bytesToPrint accordingly. In any case, bytesToPrint is the
162 * max number of valid bytes in *thing->Data.
163 */
164#define BLOB_LENGTH_PRINT	3
165
166static void printBlobBytes(
167	const char 			*blobType,
168	const char 			*quanta,		// e.g., "bytes', "bits"
169	uint32			bytesToPrint,
170	const CSSM_DATA	*thing)
171{
172	uint32 dex;
173	uint32 toPrint = bytesToPrint;
174
175	if(toPrint > BLOB_LENGTH_PRINT) {
176		toPrint = BLOB_LENGTH_PRINT;
177	}
178	printf("%s; Length %u %s; data = ",
179		blobType, (unsigned)thing->Length, quanta);
180	for(dex=0; dex<toPrint; dex++) {
181		printf("0x%x ", thing->Data[dex]);
182		if(dex == (toPrint - 1)) {
183			break;
184		}
185	}
186	if(dex < bytesToPrint) {
187		printf(" ...\n");
188	}
189	else {
190		printf("\n");
191	}
192}
193
194/*
195 * Print an IA5String or Printable string. Null terminator is not assumed.
196 * Trailing newline is printed.
197 */
198static void printString(
199	const CSSM_DATA *str)
200{
201	unsigned i;
202	char *cp = (char *)str->Data;
203	for(i=0; i<str->Length; i++) {
204		printf("%c", *cp++);
205	}
206	printf("\n");
207}
208
209static void printDerThing(
210	CSSM_BER_TAG		tagType,
211	const CSSM_DATA		*thing,
212	OidParser 			&parser)
213{
214	switch(tagType) {
215		case BER_TAG_INTEGER:
216			printf("%d\n", cuDER_ToInt(thing));
217			return;
218		case BER_TAG_BOOLEAN:
219			if(thing->Length != 1) {
220				printf("***malformed BER_TAG_BOOLEAN: length %u data ",
221					(unsigned)thing->Length);
222			}
223			printf("%u\n", cuDER_ToInt(thing));
224			return;
225		case BER_TAG_PRINTABLE_STRING:
226		case BER_TAG_IA5_STRING:
227		case BER_TAG_T61_STRING:
228		case BER_TAG_PKIX_UTF8_STRING:	// mostly printable....
229			printString(thing);
230			return;
231		case BER_TAG_OCTET_STRING:
232			printBlobBytes("Byte string", "bytes", (uint32)thing->Length, thing);
233			return;
234		case BER_TAG_BIT_STRING:
235			printBlobBytes("Bit string", "bits", (uint32)(thing->Length + 7) / 8, thing);
236			return;
237		case BER_TAG_SEQUENCE:
238			printBlobBytes("Sequence", "bytes", (uint32)thing->Length, thing);
239			return;
240		case BER_TAG_SET:
241			printBlobBytes("Set", "bytes", (uint32)thing->Length, thing);
242			return;
243		case BER_TAG_OID:
244			printf("OID = ");
245			printOid(parser, thing);
246			break;
247		default:
248			printf("not displayed (tagType = %s; length %u)\n",
249				tagTypeString(tagType), (unsigned)thing->Length);
250			break;
251
252	}
253}
254
255/* compare two OIDs, return CSSM_TRUE if identical */
256static CSSM_BOOL compareOids(
257	const CSSM_OID *oid1,
258	const CSSM_OID *oid2)
259{
260	if((oid1 == NULL) || (oid2 == NULL)) {
261		return CSSM_FALSE;
262	}
263	if(oid1->Length != oid2->Length) {
264		return CSSM_FALSE;
265	}
266	if(memcmp(oid1->Data, oid2->Data, oid1->Length)) {
267		return CSSM_FALSE;
268	}
269	else {
270		return CSSM_TRUE;
271	}
272}
273
274/*
275 * Following a CSSMOID_ECDSA_WithSpecified algorithm is another encoded
276 * CSSM_X509_ALGORITHM_IDENTIFIER containing the digest algorithm OID.
277 * Decode and print the OID.
278 */
279static void printECDSA_SigAlgParams(
280	const CSSM_DATA *params,
281	OidParser &parser)
282{
283	SecAsn1CoderRef coder = NULL;
284	if(SecAsn1CoderCreate(&coder)) {
285		printf("***Error in SecAsn1CoderCreate()\n");
286		return;
287	}
288	CSSM_X509_ALGORITHM_IDENTIFIER algParams;
289	memset(&algParams, 0, sizeof(algParams));
290	if(SecAsn1DecodeData(coder, params, kSecAsn1AlgorithmIDTemplate,
291			&algParams)) {
292		printf("***Error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n");
293		goto errOut;
294	}
295	printOid(parser, &algParams.algorithm);
296errOut:
297	SecAsn1CoderRelease(coder);
298}
299
300static void printSigAlg(
301	const CSSM_X509_ALGORITHM_IDENTIFIER *sigAlg,
302	OidParser 							&parser)
303{
304	printOid(parser, &sigAlg->algorithm);
305	if(sigAlg->parameters.Data != NULL) {
306		printf("   alg params      : ");
307		if(compareOids(&sigAlg->algorithm, &CSSMOID_ecPublicKey) &&
308		   (sigAlg->parameters.Data[0] == BER_TAG_OID) &&
309		   (sigAlg->parameters.Length > 2)) {
310			/*
311			 * An OID accompanying an ECDSA public key. The OID is an ECDSA curve.
312			 * Do a quickie DER-decode of the OID - it's here in encoded form
313			 * because this field is an ASN_ANY - and print the resulting OID.
314			 */
315			CSSM_OID curveOid = {sigAlg->parameters.Length-2, sigAlg->parameters.Data+2};
316			printOid(parser, &curveOid);
317		}
318		else if(compareOids(&sigAlg->algorithm, &CSSMOID_ECDSA_WithSpecified)) {
319			/*
320			 * The accompanying params specify the digest algorithm.
321			 */
322			printECDSA_SigAlgParams(&sigAlg->parameters, parser);
323		}
324		else {
325			/* All others - ASN_ANY - punt */
326			printDataAsHex(&sigAlg->parameters, 8);
327		}
328	}
329}
330
331static void printRdn(
332	const CSSM_X509_RDN			*rdnp,
333	OidParser 					&parser)
334{
335	CSSM_X509_TYPE_VALUE_PAIR 	*ptvp;
336	unsigned					pairDex;
337	const char						*fieldName;
338
339	for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
340		ptvp = &rdnp->AttributeTypeAndValue[pairDex];
341		if(compareOids(&ptvp->type, &CSSMOID_CountryName)) {
342			fieldName = "Country       ";
343		}
344		else if(compareOids(&ptvp->type, &CSSMOID_OrganizationName)) {
345			fieldName = "Org           ";
346		}
347		else if(compareOids(&ptvp->type, &CSSMOID_LocalityName)) {
348			fieldName = "Locality      ";
349		}
350		else if(compareOids(&ptvp->type, &CSSMOID_OrganizationalUnitName)) {
351			fieldName = "OrgUnit       ";
352		}
353		else if(compareOids(&ptvp->type, &CSSMOID_CommonName)) {
354			fieldName = "Common Name   ";
355		}
356		else if(compareOids(&ptvp->type, &CSSMOID_Surname)) {
357			fieldName = "Surname       ";
358		}
359		else if(compareOids(&ptvp->type, &CSSMOID_Title)) {
360			fieldName = "Title         ";
361		}
362		else if(compareOids(&ptvp->type, &CSSMOID_Surname)) {
363			fieldName = "Surname       ";
364		}
365		else if(compareOids(&ptvp->type, &CSSMOID_StateProvinceName)) {
366			fieldName = "State         ";
367		}
368		else if(compareOids(&ptvp->type, &CSSMOID_CollectiveStateProvinceName)) {
369			fieldName = "Coll. State   ";
370		}
371		else if(compareOids(&ptvp->type, &CSSMOID_EmailAddress)) {
372			/* deprecated, used by Thawte */
373			fieldName = "Email addrs   ";
374		}
375		else if(compareOids(&ptvp->type, &CSSMOID_Description)) {
376			fieldName = "Description   ";
377		}
378		else {
379			fieldName = "Other name    ";
380		}
381		printf("   %s  : ", fieldName);
382		printDerThing(ptvp->valueType, &ptvp->value, parser);
383	}	/* for each type/value pair */
384}
385
386static CSSM_RETURN printName(
387	const CSSM_X509_NAME	 	*x509Name,
388	OidParser 					&parser)
389{
390	CSSM_X509_RDN_PTR    		rdnp;
391	unsigned					rdnDex;
392
393	for(rdnDex=0; rdnDex<x509Name->numberOfRDNs; rdnDex++) {
394		rdnp = &x509Name->RelativeDistinguishedName[rdnDex];
395		printRdn(rdnp, parser);
396	}
397
398	return CSSM_OK;
399}
400
401static void printKeyHeader(
402	const CSSM_KEYHEADER &hdr)
403{
404	printf("   Algorithm       : ");
405	switch(hdr.AlgorithmId) {
406		case CSSM_ALGID_RSA:
407			printf("RSA\n");
408			break;
409		case CSSM_ALGID_DSA:
410			printf("DSA\n");
411			break;
412		case CSSM_ALGID_FEE:
413			printf("FEE\n");
414			break;
415		case CSSM_ALGID_DH:
416			printf("Diffie-Hellman\n");
417			break;
418		case CSSM_ALGID_ECDSA:
419			printf("ECDSA\n");
420			break;
421		default:
422			printf("Unknown(%u(d), 0x%x)\n", (unsigned)hdr.AlgorithmId,
423				(unsigned)hdr.AlgorithmId);
424	}
425	printf("   Key Size        : %u bits\n", (unsigned)hdr.LogicalKeySizeInBits);
426	printf("   Key Use         : ");
427	CSSM_KEYUSE usage = hdr.KeyUsage;
428	if(usage & CSSM_KEYUSE_ANY) {
429		printf("CSSM_KEYUSE_ANY ");
430	}
431	if(usage & CSSM_KEYUSE_ENCRYPT) {
432		printf("CSSM_KEYUSE_ENCRYPT ");
433	}
434	if(usage & CSSM_KEYUSE_DECRYPT) {
435		printf("CSSM_KEYUSE_DECRYPT ");
436	}
437	if(usage & CSSM_KEYUSE_SIGN) {
438		printf("CSSM_KEYUSE_SIGN ");
439	}
440	if(usage & CSSM_KEYUSE_VERIFY) {
441		printf("CSSM_KEYUSE_VERIFY ");
442	}
443	if(usage & CSSM_KEYUSE_SIGN_RECOVER) {
444		printf("CSSM_KEYUSE_SIGN_RECOVER ");
445	}
446	if(usage & CSSM_KEYUSE_VERIFY_RECOVER) {
447		printf("CSSM_KEYUSE_VERIFY_RECOVER ");
448	}
449	if(usage & CSSM_KEYUSE_WRAP) {
450		printf("CSSM_KEYUSE_WRAP ");
451	}
452	if(usage & CSSM_KEYUSE_UNWRAP) {
453		printf("CSSM_KEYUSE_UNWRAP ");
454	}
455	if(usage & CSSM_KEYUSE_DERIVE) {
456		printf("CSSM_KEYUSE_DERIVE ");
457	}
458	printf("\n");
459
460}
461
462/*
463 * Print contents of a CE_GeneralName as best we can.
464 */
465static void printGeneralName(
466	const CE_GeneralName	*name,
467	OidParser 				&parser)
468{
469	switch(name->nameType) {
470		case GNT_RFC822Name:
471			printf("   RFC822Name      : ");
472			printString(&name->name);
473			break;
474		case GNT_DNSName:
475			printf("   DNSName         : ");
476			printString(&name->name);
477			break;
478		case GNT_URI:
479			printf("   URI             : ");
480			printString(&name->name);
481			break;
482		case GNT_IPAddress:
483			printf("   IP Address      : ");
484			for(unsigned i=0; i<name->name.Length; i++) {
485				printf("%d", name->name.Data[i]);
486				if(i < (name->name.Length - 1)) {
487					printf(".");
488				}
489			}
490			printf("\n");
491			break;
492		case GNT_RegisteredID:
493			printf("   RegisteredID    : ");
494			printOid(parser, &name->name);
495			break;
496		case GNT_X400Address:
497			/* ORAddress, a very complicated struct - punt */
498			printf("   X400Address     : ");
499			printBlobBytes("Sequence", "bytes", (uint32)name->name.Length, &name->name);
500			break;
501		case GNT_DirectoryName:
502			if(!name->berEncoded) {
503				/* CL parsed it for us into an CSSM_X509_NAME */
504				if(name->name.Length != sizeof(CSSM_X509_NAME)) {
505					printf("***MALFORMED GNT_DirectoryName\n");
506					break;
507				}
508				const CSSM_X509_NAME *x509Name =
509					(const CSSM_X509_NAME *)name->name.Data;
510				printf("   Dir Name        :\n");
511				printName(x509Name, parser);
512			}
513			else {
514				/* encoded Name (i.e. CSSM_X509_NAME) */
515				printf("   Dir Name        : ");
516				printBlobBytes("Byte string", "bytes",
517					(uint32)name->name.Length, &name->name);
518			}
519			break;
520		case GNT_EdiPartyName:
521			/* sequence EDIPartyName */
522			printf("   EdiPartyName    : ");
523			printBlobBytes("Sequence", "bytes", (uint32)name->name.Length, &name->name);
524			break;
525		case GNT_OtherName:
526		{
527			printf("   OtherName       :\n");
528			if(name->name.Length != sizeof(CE_OtherName)) {
529				printf("***Malformed CE_OtherName\n");
530				break;
531			}
532			CE_OtherName *other = (CE_OtherName *)name->name.Data;
533			printf("      typeID       : ");
534			printOid(parser, &other->typeId);
535			printf("      value        : ");
536			printDataAsHex(&other->value, 0);
537			break;
538		}
539	}
540}
541
542
543/*
544 * Print contents of a CE_GeneralNames as best we can.
545 */
546static void printGeneralNames(
547	const CE_GeneralNames	*generalNames,
548	OidParser 				&parser)
549{
550	unsigned			i;
551	CE_GeneralName		*name;
552
553	for(i=0; i<generalNames->numNames; i++) {
554		name = &generalNames->generalName[i];
555		printGeneralName(name, parser);
556	}
557}
558
559static int printCdsaExtensionCommon(
560	const CSSM_X509_EXTENSION 	*cssmExt,
561	OidParser					&parser,
562	bool						expectParsed,
563	CSSM_BOOL					verbose,
564	bool						extraIndent = false)
565{
566	if(extraIndent) {
567		printf("   Extension       : "); printOid(parser, &cssmExt->extnId);
568		printf("      Critical     : %s\n", cssmExt->critical ? "TRUE" : "FALSE");
569	}
570	else {
571		printf("Extension struct   : "); printOid(parser, &cssmExt->extnId);
572		printf("   Critical        : %s\n", cssmExt->critical ? "TRUE" : "FALSE");
573	}
574
575	/* currently (since Radar 3593624), these are both always valid */
576	#if 0
577	/* this prevents printing pre-encoded extensions in clxutils/extenTest */
578	if((cssmExt->BERvalue.Data == NULL) ||
579	   (cssmExt->value.parsedValue == NULL)) {  /* actually, one of three variants */
580		printf("***Malformed CSSM_X509_EXTENSION (1)\n");
581		return 1;
582	}
583	#endif
584	switch(cssmExt->format) {
585		case CSSM_X509_DATAFORMAT_ENCODED:
586			if(expectParsed) {
587				printf("Bad CSSM_X509_EXTENSION; expected FORMAT_PARSED\n");
588				return 1;
589			}
590			break;
591		case CSSM_X509_DATAFORMAT_PARSED:
592			if(!expectParsed) {
593				printf("Bad CSSM_X509_EXTENSION; expected FORMAT_ENCODED\n");
594				return 1;
595			}
596			break;
597		case CSSM_X509_DATAFORMAT_PAIR:
598			/* unsupported */
599			printf("Bad CSSM_X509_EXTENSION format:FORMAT_PAIR\n");
600			return 1;
601		default:
602			printf("***Unknown CSSM_X509_EXTENSION.format\n");
603			return 1;
604	}
605	return 0;
606}
607
608static int printExtensionCommon(
609	const CSSM_DATA		&value,
610	OidParser			&parser,
611	CSSM_BOOL			verbose,
612	bool				expectParsed = true)
613{
614	if(value.Length != sizeof(CSSM_X509_EXTENSION)) {
615		printf("***malformed CSSM_FIELD (1)\n");
616		return 1;
617	}
618	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
619	return printCdsaExtensionCommon(cssmExt, parser, expectParsed, verbose);
620}
621
622
623static void printKeyUsage(
624	const CSSM_DATA &value)
625{
626	CE_KeyUsage usage;
627	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
628
629	usage = *((CE_KeyUsage *)cssmExt->value.parsedValue);
630	printf("   usage           : ");
631	if(usage & CE_KU_DigitalSignature) {
632		printf("DigitalSignature ");
633	}
634	if(usage & CE_KU_NonRepudiation) {
635		printf("NonRepudiation ");
636	}
637	if(usage & CE_KU_KeyEncipherment) {
638		printf("KeyEncipherment ");
639	}
640	if(usage & CE_KU_DataEncipherment) {
641		printf("DataEncipherment ");
642	}
643	if(usage & CE_KU_KeyAgreement) {
644		printf("KeyAgreement ");
645	}
646	if(usage & CE_KU_KeyCertSign) {
647		printf("KeyCertSign ");
648	}
649	if(usage & CE_KU_CRLSign) {
650		printf("CRLSign ");
651	}
652	if(usage & CE_KU_EncipherOnly) {
653		printf("EncipherOnly ");
654	}
655	if(usage & CE_KU_DecipherOnly) {
656		printf("DecipherOnly ");
657	}
658	printf("\n");
659
660}
661
662static void printBasicConstraints(
663	const CSSM_DATA &value)
664{
665	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
666	CE_BasicConstraints *bc = (CE_BasicConstraints *)cssmExt->value.parsedValue;
667	printf("   CA              : %s\n", bc->cA ? "TRUE" : "FALSE");
668	if(bc->pathLenConstraintPresent) {
669		printf("   pathLenConstr   : %u\n", (unsigned)bc->pathLenConstraint);
670	}
671}
672
673static void printExtKeyUsage(
674	const CSSM_DATA 	&value,
675	OidParser 			&parser)
676{
677	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
678	CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)cssmExt->value.parsedValue;
679	unsigned oidDex;
680	for(oidDex=0; oidDex<eku->numPurposes; oidDex++) {
681		printf("   purpose %2d      : ", oidDex);
682		printOid(parser, &eku->purposes[oidDex]);
683	}
684}
685
686static void printCssmAuthorityKeyId(
687	const CE_AuthorityKeyID *akid,
688	OidParser 				&parser)
689{
690	if(akid->keyIdentifierPresent) {
691		printf("   Auth KeyID      : ");
692		printDataAsHex(&akid->keyIdentifier,
6938);
694	}
695	if(akid->generalNamesPresent) {
696		printGeneralNames(akid->generalNames, parser);
697	}
698	if(akid->serialNumberPresent) {
699		printf("   serialNumber    : ");
700		printDataAsHex(&akid->serialNumber, 8);
701	}
702}
703
704static void printAuthorityKeyId(
705	const CSSM_DATA 	&value,
706	OidParser 			&parser)
707{
708	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
709	CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)cssmExt->value.parsedValue;
710	printCssmAuthorityKeyId(akid, parser);
711}
712
713static void printSubjectIssuerAltName(
714	const CSSM_DATA 	&value,
715	OidParser 			&parser)
716{
717	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
718	CE_GeneralNames *san = (CE_GeneralNames *)cssmExt->value.parsedValue;
719	printGeneralNames(san, parser);
720}
721
722static void printDistPointName(
723	const CE_DistributionPointName	*dpn,
724	OidParser						&parser)
725{
726	switch(dpn->nameType) {
727		case CE_CDNT_FullName:
728			printGeneralNames(dpn->dpn.fullName, parser);
729			break;
730		case CE_CDNT_NameRelativeToCrlIssuer:
731			printRdn(dpn->dpn.rdn, parser);
732			break;
733		default:
734			printf("***BOGUS CE_DistributionPointName.nameType\n");
735			break;
736	}
737}
738
739static void printDistPoint(
740	const CE_CRLDistributionPoint	*dp,
741	OidParser						&parser)
742{
743	if(dp->distPointName) {
744		printf("   Dist pt Name    :\n");
745		printDistPointName(dp->distPointName, parser);
746	}
747	printf("   reasonsPresent  : %s\n", dp->reasonsPresent ? "TRUE" : "FALSE");
748	if(dp->reasonsPresent) {
749		/* FIXME - parse */
750		printf("  reasons           : 0x%X\n", dp->reasons);
751	}
752	if(dp->crlIssuer) {
753		printf("  CRLIssuer        :\n");
754		printGeneralNames(dp->crlIssuer, parser);
755	}
756}
757
758static void printDistributionPoints(
759	const CSSM_DATA 	&value,
760	OidParser 			&parser)
761{
762	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
763	CE_CRLDistPointsSyntax *dps = (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
764
765	for(unsigned dex=0; dex<dps->numDistPoints; dex++) {
766		printf("   Dist pt %d       :\n", dex);
767		printDistPoint(&dps->distPoints[dex], parser);
768	}
769}
770
771static void printValueOrNotPresent(
772	CSSM_BOOL present,
773	CSSM_BOOL value)
774{
775	if(!present) {
776		printf("<Not Present>\n");
777	}
778	else if(value) {
779		printf("TRUE\n");
780	}
781	else {
782		printf("FALSE");
783	}
784}
785
786static void printIssuingDistributionPoint(
787	const CE_IssuingDistributionPoint 	*idp,
788	OidParser 							&parser)
789{
790	if(idp->distPointName) {
791		printf("   Dist pt          :\n");
792		printDistPointName(idp->distPointName, parser);
793	}
794	printf("   Only user certs : ");
795	printValueOrNotPresent(idp->onlyUserCertsPresent, idp->onlyUserCerts);
796	printf("   Only CA certs   : ");
797	printValueOrNotPresent(idp->onlyCACertsPresent, idp->onlyCACerts);
798	printf("   Only some reason: ");
799	printValueOrNotPresent(idp->onlySomeReasonsPresent, idp->onlySomeReasons);
800	printf("   Indirectl CRL   : ");
801	printValueOrNotPresent(idp->indirectCrlPresent, idp->indirectCrl);
802}
803
804static void printCertPolicies(
805	const CSSM_DATA 	&value,
806	OidParser 			&parser)
807{
808	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
809	CE_CertPolicies *cdsaObj = (CE_CertPolicies *)cssmExt->value.parsedValue;
810	for(unsigned polDex=0; polDex<cdsaObj->numPolicies; polDex++) {
811		CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex];
812		printf("   Policy %2d       : ID ", polDex);
813		printOid(parser, &cPolInfo->certPolicyId);
814		for(unsigned qualDex=0; qualDex<cPolInfo->numPolicyQualifiers; qualDex++) {
815			CE_PolicyQualifierInfo *cQualInfo = &cPolInfo->policyQualifiers[qualDex];
816			printf("      Qual %2d      : ID ", qualDex);
817			printOid(parser, &cQualInfo->policyQualifierId);
818			if(cuCompareCssmData(&cQualInfo->policyQualifierId,
819					&CSSMOID_QT_CPS)) {
820				printf("         CPS       : ");
821				printString(&cQualInfo->qualifier);
822			}
823			else {
824				printf("         unparsed  : ");
825				printDataAsHex(&cQualInfo->qualifier, 8);
826			}
827		}
828	}
829}
830
831static void printNetscapeCertType(
832	const CSSM_DATA &value)
833{
834	CE_NetscapeCertType certType;
835	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
836
837	certType = *((CE_NetscapeCertType *)cssmExt->value.parsedValue);
838	printf("   certType        : ");
839	if(certType & CE_NCT_SSL_Client) {
840		printf("SSL_Client ");
841	}
842	if(certType & CE_NCT_SSL_Server) {
843		printf("SSL_Server ");
844	}
845	if(certType & CE_NCT_SMIME) {
846		printf("S/MIME ");
847	}
848	if(certType & CE_NCT_ObjSign) {
849		printf("ObjectSign ");
850	}
851	if(certType & CE_NCT_Reserved) {
852		printf("Reserved ");
853	}
854	if(certType & CE_NCT_SSL_CA) {
855		printf("SSL_CA ");
856	}
857	if(certType & CE_NCT_SMIME_CA) {
858		printf("SMIME_CA ");
859	}
860	if(certType & CE_NCT_ObjSignCA) {
861		printf("ObjSignCA ");
862	}
863	printf("\n");
864}
865
866static void printAuthorityInfoAccess(
867	const CSSM_DATA 	&value,
868	OidParser 			&parser)
869{
870	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
871	CE_AuthorityInfoAccess	*info = (CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
872
873	printf("   numDescriptions : %lu\n", (unsigned long)info->numAccessDescriptions);
874	for(unsigned dex=0; dex<info->numAccessDescriptions; dex++) {
875		printf("   description %u   : \n", dex);
876		printf("   accessMethod    : ");
877		CE_AccessDescription *descr = &info->accessDescriptions[dex];
878		printOid(parser, &descr->accessMethod);
879		printGeneralName(&descr->accessLocation, parser);
880	}
881}
882
883static void printQualCertStatements(
884	const CSSM_DATA 	&value,
885	OidParser 			&parser)
886{
887	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)value.Data;
888	CE_QC_Statements	*qcss = (CE_QC_Statements *)cssmExt->value.parsedValue;
889
890	printf("   numQCStatements : %lu\n", (unsigned long)qcss->numQCStatements);
891	for(unsigned dex=0; dex<qcss->numQCStatements; dex++) {
892		CE_QC_Statement *qcs = &qcss->qcStatements[dex];
893
894		printf("   statement %u     : \n", dex);
895		printf("   statementId     : ");
896		printOid(parser, &qcs->statementId);
897		if(qcs->semanticsInfo) {
898			printf("   semanticsInfo   :\n");
899			CE_SemanticsInformation *si = qcs->semanticsInfo;
900			if(si->semanticsIdentifier) {
901				printf("   semanticsId     : ");
902				printOid(parser, si->semanticsIdentifier);
903			}
904			if(si->nameRegistrationAuthorities) {
905				printf("   nameRegAuth     :\n");
906				printGeneralNames(si->nameRegistrationAuthorities, parser);
907			}
908		}
909		if(qcs->otherInfo) {
910			printf("   otherInfo       : "); printDataAsHex(qcs->otherInfo, 8);
911		}
912	}
913}
914
915/* print one field */
916void printCertField(
917	const CSSM_FIELD 	&field,
918	OidParser 			&parser,
919	CSSM_BOOL			verbose)
920{
921	const CSSM_DATA *thisData = &field.FieldValue;
922	const CSSM_OID  *thisOid = &field.FieldOid;
923
924	if(cuCompareCssmData(thisOid, &CSSMOID_X509V1Version)) {
925		if(verbose) {
926			printf("Version            : %u\n", cuDER_ToInt(thisData));
927		}
928	}
929	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SerialNumber)) {
930		printf("Serial Number      : "); printDataAsHex(thisData, 0);
931	}
932	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1IssuerNameCStruct)) {
933		printf("Issuer Name        :\n");
934		CSSM_X509_NAME_PTR name = (CSSM_X509_NAME_PTR)thisData->Data;
935		if((name == NULL) || (thisData->Length != sizeof(CSSM_X509_NAME))) {
936			printf("   ***malformed CSSM_X509_NAME\n");
937		}
938		else {
939			printName(name, parser);
940		}
941	}
942	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SubjectNameCStruct)) {
943		printf("Subject Name       :\n");
944		CSSM_X509_NAME_PTR name = (CSSM_X509_NAME_PTR)thisData->Data;
945		if((name == NULL) || (thisData->Length != sizeof(CSSM_X509_NAME))) {
946			printf("   ***malformed CSSM_X509_NAME\n");
947		}
948		else {
949			printName(name, parser);
950		}
951	}
952	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1ValidityNotBefore)) {
953		CSSM_X509_TIME *cssmTime = (CSSM_X509_TIME *)thisData->Data;
954		if((cssmTime == NULL) || (thisData->Length != sizeof(CSSM_X509_TIME))) {
955			printf("   ***malformed CSSM_X509_TIME\n");
956		}
957		else if(verbose) {
958			printf("Not Before         : "); printString(&cssmTime->time);
959			printf("                   : ");
960			printTime(cssmTime);
961		}
962		else {
963			printf("Not Before         : ");
964			printTime(cssmTime);
965		}
966	}
967	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1ValidityNotAfter)) {
968		CSSM_X509_TIME *cssmTime = (CSSM_X509_TIME *)thisData->Data;
969		if((cssmTime == NULL) || (thisData->Length != sizeof(CSSM_X509_TIME))) {
970			printf("   ***malformed CSSM_X509_TIME\n");
971		}
972		else if(verbose) {
973			printf("Not After          : "); printString(&cssmTime->time);
974			printf("                   : ");
975			printTime(cssmTime);
976		}
977		else {
978			printf("Not After          : ");
979			printTime(cssmTime);
980		}
981	}
982	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SignatureAlgorithmTBS)) {
983		if(verbose) {
984			/* normally skip, it's the same as TBS sig alg */
985			printf("TBS Sig Algorithm  : ");
986			CSSM_X509_ALGORITHM_IDENTIFIER *algId =
987				(CSSM_X509_ALGORITHM_IDENTIFIER *)thisData->Data;
988			if((algId == NULL) ||
989			(thisData->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER))) {
990				printf("   ***malformed CSSM_X509_ALGORITHM_IDENTIFIER\n");
991			}
992			else {
993				printSigAlg(algId, parser);
994			}
995		}
996	}
997	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SignatureAlgorithm)) {
998		printf("Cert Sig Algorithm : ");
999		CSSM_X509_ALGORITHM_IDENTIFIER *algId =
1000			(CSSM_X509_ALGORITHM_IDENTIFIER *)thisData->Data;
1001		if((algId == NULL) ||
1002		   (thisData->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER))) {
1003			printf("   ***malformed CSSM_X509_ALGORITHM_IDENTIFIER\n");
1004		}
1005		else {
1006			printSigAlg(algId, parser);
1007		}
1008	}
1009	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1CertificateIssuerUniqueId)) {
1010		if(verbose) {
1011			printf("Issuer UniqueId    : ");
1012			printDerThing(BER_TAG_BIT_STRING, thisData, parser);
1013		}
1014	}
1015	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1CertificateSubjectUniqueId)) {
1016		if(verbose) {
1017			printf("Subject UniqueId   : ");
1018			printDerThing(BER_TAG_BIT_STRING, thisData, parser);
1019		}
1020	}
1021	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SubjectPublicKeyCStruct)) {
1022		CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *pubKeyInfo =
1023			(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)thisData->Data;
1024		printf("Pub Key Algorithm  : ");
1025		if((pubKeyInfo == NULL) ||
1026		   (thisData->Length != sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO))) {
1027			printf("   ***malformed CSSM_X509_SUBJECT_PUBLIC_KEY_INFO\n");
1028		}
1029		else {
1030			printSigAlg(&pubKeyInfo->algorithm, parser);
1031			printf("Pub key Bytes      : Length %u bytes : ",
1032				(unsigned)pubKeyInfo->subjectPublicKey.Length);
1033			printDataAsHex(&pubKeyInfo->subjectPublicKey, 8);
1034		}
1035	}
1036	else if(cuCompareCssmData(thisOid, &CSSMOID_CSSMKeyStruct)) {
1037		CSSM_KEY_PTR cssmKey =  (CSSM_KEY_PTR)thisData->Data;
1038		printf("CSSM Key           :\n");
1039		if((cssmKey == NULL) ||
1040		   (thisData->Length != sizeof(CSSM_KEY))) {
1041			printf("   ***malformed CSSM_KEY\n");
1042		}
1043		else {
1044			printKeyHeader(cssmKey->KeyHeader);
1045			if(verbose) {
1046				printf("   Key Blob        : ");
1047				printDataAsHex(&cssmKey->KeyData, 8);
1048			}
1049		}
1050	}
1051	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1Signature)) {
1052		printf("Signature          : %u bytes : ", (unsigned)thisData->Length);
1053		printDataAsHex(thisData, 8);
1054	}
1055	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V3CertificateExtensionCStruct)) {
1056		if(printExtensionCommon(*thisData, parser, verbose, false)) {
1057			return;
1058		}
1059		CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)thisData->Data;
1060		printf("   Unparsed data   : "); printDataAsHex(&cssmExt->BERvalue, 8);
1061	}
1062	else if(cuCompareCssmData(thisOid, &CSSMOID_KeyUsage)) {
1063		if(printExtensionCommon(*thisData, parser, verbose)) {
1064			return;
1065		}
1066		printKeyUsage(*thisData);
1067	}
1068	else if(cuCompareCssmData(thisOid, &CSSMOID_BasicConstraints)) {
1069		if(printExtensionCommon(*thisData, parser, verbose)) {
1070			return;
1071		}
1072		printBasicConstraints(*thisData);
1073	}
1074	else if(cuCompareCssmData(thisOid, &CSSMOID_ExtendedKeyUsage)) {
1075		if(printExtensionCommon(*thisData, parser, verbose)) {
1076			return;
1077		}
1078		printExtKeyUsage(*thisData, parser);
1079	}
1080	else if(cuCompareCssmData(thisOid, &CSSMOID_SubjectKeyIdentifier)) {
1081		if(printExtensionCommon(*thisData, parser, verbose)) {
1082			return;
1083		}
1084		CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)thisData->Data;
1085		CSSM_DATA_PTR cdata = (CSSM_DATA_PTR)cssmExt->value.parsedValue;
1086		if((cdata == NULL) || (cdata->Data == NULL)) {
1087			printf("****Malformed extension (no parsedValue)\n");
1088		}
1089		else {
1090			printf("   Subject KeyID   : "); printDataAsHex(cdata, 8);
1091		}
1092	}
1093	else if(cuCompareCssmData(thisOid, &CSSMOID_AuthorityKeyIdentifier)) {
1094		if(printExtensionCommon(*thisData, parser, verbose)) {
1095			return;
1096		}
1097		printAuthorityKeyId(*thisData, parser);
1098	}
1099	else if(cuCompareCssmData(thisOid, &CSSMOID_SubjectAltName)) {
1100		if(printExtensionCommon(*thisData, parser, verbose)) {
1101			return;
1102		}
1103		printSubjectIssuerAltName(*thisData, parser);
1104	}
1105	else if(cuCompareCssmData(thisOid, &CSSMOID_IssuerAltName)) {
1106		if(printExtensionCommon(*thisData, parser, verbose)) {
1107			return;
1108		}
1109		printSubjectIssuerAltName(*thisData, parser);
1110	}
1111	else if(cuCompareCssmData(thisOid, &CSSMOID_CertificatePolicies)) {
1112		if(printExtensionCommon(*thisData, parser, verbose)) {
1113			return;
1114		}
1115		printCertPolicies(*thisData, parser);
1116	}
1117	else if(cuCompareCssmData(thisOid, &CSSMOID_NetscapeCertType)) {
1118		if(printExtensionCommon(*thisData, parser, verbose)) {
1119			return;
1120		}
1121		printNetscapeCertType(*thisData);
1122	}
1123	else if(cuCompareCssmData(thisOid, &CSSMOID_CrlDistributionPoints)) {
1124		if(printExtensionCommon(*thisData, parser, verbose)) {
1125			return;
1126		}
1127		printDistributionPoints(*thisData, parser);
1128	}
1129	else if(cuCompareCssmData(thisOid, &CSSMOID_AuthorityInfoAccess)) {
1130		if(printExtensionCommon(*thisData, parser, verbose)) {
1131			return;
1132		}
1133		printAuthorityInfoAccess(*thisData, parser);
1134	}
1135	else if(cuCompareCssmData(thisOid, &CSSMOID_SubjectInfoAccess)) {
1136		if(printExtensionCommon(*thisData, parser, verbose)) {
1137			return;
1138		}
1139		printAuthorityInfoAccess(*thisData, parser);
1140	}
1141	else if(cuCompareCssmData(thisOid, &CSSMOID_QC_Statements)) {
1142		if(printExtensionCommon(*thisData, parser, verbose)) {
1143			return;
1144		}
1145		printQualCertStatements(*thisData, parser);
1146	}
1147	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1IssuerName)) {
1148		if(verbose) {
1149			printf("Normalized Issuer  : ");
1150			printDataAsHex(thisData, 8);
1151		}
1152	}
1153	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SubjectName)) {
1154		if(verbose) {
1155			printf("Normalized Subject : ");
1156			printDataAsHex(thisData, 8);
1157		}
1158	}
1159	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1IssuerNameStd)) {
1160		if(verbose) {
1161			printf("DER-encoded issuer : ");
1162			printDataAsHex(thisData, 8);
1163		}
1164	}
1165	else if(cuCompareCssmData(thisOid, &CSSMOID_X509V1SubjectNameStd)) {
1166		if(verbose) {
1167			printf("DER-encoded subject: ");
1168			printDataAsHex(thisData, 8);
1169		}
1170	}
1171	else {
1172		printf("Other field:       : "); printOid(parser, thisOid);
1173	}
1174}
1175
1176static
1177void printCrlExten(
1178	const CSSM_X509_EXTENSION *exten,
1179	CSSM_BOOL			verbose,
1180	OidParser 			&parser)
1181{
1182	const CSSM_OID *oid = &exten->extnId;
1183	const void *thisData = exten->value.parsedValue;
1184
1185	if(exten->format == CSSM_X509_DATAFORMAT_ENCODED) {
1186		if(printCdsaExtensionCommon(exten, parser, false, verbose)) {
1187			return;
1188		}
1189		printf("   Unparsed data   : "); printDataAsHex(&exten->BERvalue, 8);
1190	}
1191	else if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
1192		printf("***Badly formatted CSSM_X509_EXTENSION\n");
1193		return;
1194	}
1195	else if(cuCompareCssmData(oid, &CSSMOID_AuthorityKeyIdentifier)) {
1196		if(printCdsaExtensionCommon(exten, parser, true, verbose)) {
1197			return;
1198		}
1199		printCssmAuthorityKeyId((CE_AuthorityKeyID *)thisData, parser);
1200	}
1201	else if(cuCompareCssmData(oid, &CSSMOID_IssuerAltName)) {
1202		if(printCdsaExtensionCommon(exten, parser, true, verbose)) {
1203			return;
1204		}
1205		printGeneralNames((CE_GeneralNames *)thisData, parser);
1206	}
1207	else if(cuCompareCssmData(oid, &CSSMOID_CrlNumber)) {
1208		if(printCdsaExtensionCommon(exten, parser, true, verbose)) {
1209			return;
1210		}
1211		printf("   CRL Number      : %u\n", *((unsigned *)thisData));
1212	}
1213	else if(cuCompareCssmData(oid, &CSSMOID_DeltaCrlIndicator)) {
1214		if(printCdsaExtensionCommon(exten, parser, true, verbose)) {
1215			return;
1216		}
1217		printf("   Delta CRL Base  : %u\n", *((unsigned *)thisData));
1218	}
1219	else if(cuCompareCssmData(oid, &CSSMOID_IssuingDistributionPoint)) {
1220		if(printCdsaExtensionCommon(exten, parser, true, verbose)) {
1221			return;
1222		}
1223		printIssuingDistributionPoint((CE_IssuingDistributionPoint *)thisData,
1224			parser);
1225	}
1226	else {
1227		/* should never happen - we're out of sync with the CL */
1228		printf("UNKNOWN EXTENSION  : "); printOid(parser, oid);
1229	}
1230}
1231
1232
1233static
1234void printCrlEntryExten(
1235	const CSSM_X509_EXTENSION *exten,
1236	CSSM_BOOL			verbose,
1237	OidParser 			&parser)
1238{
1239	const CSSM_OID *oid = &exten->extnId;
1240	const void *thisData = exten->value.parsedValue;
1241
1242	if(exten->format == CSSM_X509_DATAFORMAT_ENCODED) {
1243		if(printCdsaExtensionCommon(exten, parser, false, verbose, true)) {
1244			return;
1245		}
1246		printf("      Unparsed data: "); printDataAsHex(&exten->BERvalue, 8);
1247	}
1248	else if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
1249		printf("***Badly formatted CSSM_X509_EXTENSION\n");
1250		return;
1251	}
1252	else if(cuCompareCssmData(oid, &CSSMOID_CrlReason)) {
1253		if(printCdsaExtensionCommon(exten, parser, true, verbose, true)) {
1254			return;
1255		}
1256		CE_CrlReason *cr = (CE_CrlReason *)thisData;
1257		const char *reason = "UNKNOWN";
1258		switch(*cr) {
1259			case CE_CR_Unspecified:
1260				reason = "CE_CR_Unspecified"; break;
1261			case CE_CR_KeyCompromise:
1262				reason = "CE_CR_KeyCompromise"; break;
1263			case CE_CR_CACompromise:
1264				reason = "CE_CR_CACompromise"; break;
1265			case CE_CR_AffiliationChanged:
1266				reason = "CE_CR_AffiliationChanged"; break;
1267			case CE_CR_Superseded:
1268				reason = "CE_CR_Superseded"; break;
1269			case CE_CR_CessationOfOperation:
1270				reason = "CE_CR_CessationOfOperation"; break;
1271			case CE_CR_CertificateHold:
1272				reason = "CE_CR_CertificateHold"; break;
1273			case CE_CR_RemoveFromCRL:
1274				reason = "CE_CR_RemoveFromCRL"; break;
1275			default:
1276				break;
1277		}
1278		printf("      CRL Reason   : %s\n", reason);
1279	}
1280	else if(cuCompareCssmData(oid, &CSSMOID_HoldInstructionCode)) {
1281		if(printCdsaExtensionCommon(exten, parser, true, verbose, true)) {
1282			return;
1283		}
1284		printf("      Hold Instr   : ");
1285		printOid(parser, (CSSM_OID_PTR)thisData);
1286	}
1287	else if(cuCompareCssmData(oid, &CSSMOID_InvalidityDate)) {
1288		if(printCdsaExtensionCommon(exten, parser, true, verbose, true)) {
1289			return;
1290		}
1291		printf("      Invalid Date : ");
1292		printTimeStr((CSSM_DATA_PTR)thisData);
1293	}
1294	else if(cuCompareCssmData(oid, &CSSMOID_CertIssuer)) {
1295		if(printCdsaExtensionCommon(exten, parser, true, verbose, true)) {
1296			return;
1297		}
1298		printGeneralNames((CE_GeneralNames *)thisData, parser);
1299	}
1300	else {
1301		/* should never happen - we're out of sync with the CL */
1302		printf("UNKNOWN EXTENSION  : "); printOid(parser, oid);
1303	}
1304}
1305
1306static
1307void printCrlFields(
1308	const CSSM_X509_SIGNED_CRL *signedCrl,
1309	CSSM_BOOL					verbose,
1310	OidParser 					&parser)
1311{
1312	unsigned i;
1313	const CSSM_X509_TBS_CERTLIST *tbsCrl = &signedCrl->tbsCertList;
1314
1315	if(tbsCrl->version.Data) {
1316		printf("Version            : %d\n", cuDER_ToInt(&tbsCrl->version));
1317	}
1318
1319	printf("TBS Sig Algorithm  : ");
1320	const CSSM_X509_ALGORITHM_IDENTIFIER *algId = &tbsCrl->signature;
1321	printSigAlg(algId, parser);
1322
1323	printf("Issuer Name        :\n");
1324	printName(&tbsCrl->issuer, parser);
1325
1326	printf("This Update        : ");
1327	printTime(&tbsCrl->thisUpdate);
1328	printf("Next Update        : ");
1329	if(tbsCrl->nextUpdate.time.Data) {
1330		printTime(&tbsCrl->nextUpdate);
1331	}
1332	else {
1333		printf("<not present>\n");
1334	}
1335
1336	CSSM_X509_REVOKED_CERT_LIST_PTR certList = tbsCrl->revokedCertificates;
1337	if(certList) {
1338		if(verbose) {
1339			printf("Num Revoked Certs  : %d\n",
1340				(int)certList->numberOfRevokedCertEntries);
1341			for(i=0; i<certList->numberOfRevokedCertEntries; i++) {
1342				CSSM_X509_REVOKED_CERT_ENTRY_PTR entry;
1343				entry = &certList->revokedCertEntry[i];
1344				printf("Revoked Cert %d     :\n", (int)i);
1345				printf("   Serial number   : ");
1346				printDataAsHex(&entry->certificateSerialNumber, 0);
1347				printf("   Revocation time : ");
1348				printTime(&entry->revocationDate);
1349				const CSSM_X509_EXTENSIONS *cssmExtens = &entry->extensions;
1350				uint32 numExtens = cssmExtens->numberOfExtensions;
1351				if(numExtens == 0) {
1352					continue;
1353				}
1354				printf("   Num Extensions  : %u\n", (unsigned)numExtens);
1355				for(unsigned dex=0; dex<numExtens; dex++) {
1356					printCrlEntryExten(&cssmExtens->extensions[dex], verbose,
1357						parser);
1358				}
1359			}
1360		}
1361		else {
1362			printf("Num Revoked Certs  : %d (use verbose option to see)\n",
1363				(int)certList->numberOfRevokedCertEntries);
1364		}
1365	}
1366
1367	const CSSM_X509_EXTENSIONS *crlExtens = &tbsCrl->extensions;
1368	if(crlExtens->numberOfExtensions) {
1369		printf("Num CRL Extensions : %d\n",
1370			(int)crlExtens->numberOfExtensions);
1371		for(i=0; i<crlExtens->numberOfExtensions; i++) {
1372			printCrlExten(&crlExtens->extensions[i], verbose, parser);
1373		}
1374	}
1375
1376	const CSSM_X509_SIGNATURE *sig = &signedCrl->signature;
1377	if(sig->encrypted.Data) {
1378		printf("Signature          : %u bytes : ", (unsigned)sig->encrypted.Length);
1379		printDataAsHex(&sig->encrypted, 8);
1380	}
1381}
1382
1383
1384/* connect to CSSM/CL lazily, once */
1385static CSSM_CL_HANDLE clHand = 0;
1386
1387int printCert(
1388	const unsigned char	*certData,
1389	unsigned		certLen,
1390	CSSM_BOOL		verbose)
1391{
1392	CSSM_FIELD_PTR				fieldPtr;		// mallocd by CL
1393	uint32						i;
1394	uint32						numFields;
1395	OidParser 					parser;
1396	CSSM_DATA					cert;
1397
1398	if(clHand == 0) {
1399		clHand = cuClStartup();
1400		if(clHand == 0) {
1401			printf("***Error connecting to CSSM cert module; aborting cert "
1402				"display\n");
1403			return 0;
1404		}
1405	}
1406	cert.Data = (uint8 *)certData;
1407	cert.Length = certLen;
1408
1409	CSSM_RETURN crtn = CSSM_CL_CertGetAllFields(clHand,
1410		&cert,
1411		&numFields,
1412		&fieldPtr);
1413	if(crtn) {
1414		cuPrintError("CSSM_CL_CertGetAllFields", crtn);
1415		return crtn;
1416	}
1417
1418	for(i=0; i<numFields; i++) {
1419		printCertField(fieldPtr[i], parser, verbose);
1420	}
1421
1422	crtn = CSSM_CL_FreeFields(clHand, numFields, &fieldPtr);
1423	if(crtn) {
1424		cuPrintError("CSSM_CL_FreeFields", crtn);
1425		return crtn;
1426	}
1427	return 0;
1428}
1429
1430/* parse CRL */
1431/* This one's easier, we just get one field - the whole parsed CRL */
1432int printCrl(
1433	const  unsigned char 	*crlData,
1434	unsigned				crlLen,
1435	CSSM_BOOL				verbose)
1436{
1437	CSSM_DATA_PTR				value;		// mallocd by CL
1438	uint32						numFields;
1439	OidParser 					parser;
1440	CSSM_DATA					crl;
1441	CSSM_HANDLE					result;
1442
1443	if(clHand == 0) {
1444		clHand = cuClStartup();
1445		if(clHand == 0) {
1446			printf("***Error connecting to CSSM cert module; aborting CRL"
1447				"display\n");
1448			return 0;
1449		}
1450	}
1451	crl.Data = (uint8 *)crlData;
1452	crl.Length = crlLen;
1453
1454	CSSM_RETURN crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
1455		&crl,
1456		&CSSMOID_X509V2CRLSignedCrlCStruct,
1457		&result,
1458		&numFields,
1459		&value);
1460	if(crtn) {
1461		cuPrintError("CSSM_CL_CrlGetFirstFieldValue", crtn);
1462		return crtn;
1463	}
1464	if(numFields != 1) {
1465		printf("***CSSM_CL_CrlGetFirstFieldValue: numFields error\n");
1466		printf("   expected 1, got %d\n", (int)numFields);
1467		return 1;
1468	}
1469	crtn = CSSM_CL_CrlAbortQuery(clHand, result);
1470	if(crtn) {
1471		cuPrintError("CSSM_CL_CertAbortQuery", crtn);
1472		return crtn;
1473	}
1474
1475	if(value == NULL) {
1476		printf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
1477		return 1;
1478	}
1479	if((value->Data == NULL) ||
1480	   (value->Length != sizeof(CSSM_X509_SIGNED_CRL))) {
1481		printf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
1482		return 1;
1483	}
1484	const CSSM_X509_SIGNED_CRL *signedCrl =
1485		(const CSSM_X509_SIGNED_CRL *)value->Data;
1486	printCrlFields(signedCrl, verbose, parser);
1487
1488	crtn = CSSM_CL_FreeFieldValue(clHand,
1489		&CSSMOID_X509V2CRLSignedCrlCStruct,
1490		value);
1491	if(crtn) {
1492		cuPrintError("CSSM_CL_FreeFieldValue", crtn);
1493		return crtn;
1494	}
1495	return 0;
1496}
1497
1498
1499void printCertShutdown()
1500{
1501	if(clHand != 0) {
1502		CSSM_ModuleDetach(clHand);
1503	}
1504}
1505