1/*
2 * cmstool.cpp - manipulate CMS messages, CMSEncoder/CMSDecoder version
3 */
4
5#include <Security/Security.h>
6#include <security_cdsa_utils/cuFileIo.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include <utilLib/common.h>
12#include <security_cdsa_utils/cuFileIo.h>
13#include <security_cdsa_utils/cuPrintCert.h>
14#include <clAppUtils/identPicker.h>
15#include <clAppUtils/sslAppUtils.h>
16#include <security_cdsa_utils/cuOidParser.h>
17#include <CoreFoundation/CoreFoundation.h>
18#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
19
20#include <Security/SecTrustPriv.h>		/* SecTrustGetCssmResultCode */
21#include <Security/SecIdentityPriv.h>	/* SecIdentityCreateWithCertificate */
22#include <Security/CMSEncoder.h>
23#include <Security/CMSDecoder.h>
24#include <Security/CMSPrivate.h>
25
26#include <Security/SecCertificate.h>
27#include <Security/oidsattr.h>
28
29#define CFRELEASE(cfr)	if(cfr != NULL) { CFRelease(cfr); }
30
31static SecKeychainRef keychain_open(const char *name);
32
33static void usage(char **argv)
34{
35    printf("Usage: %s cmd [option ...]\n", argv[0]);
36	printf("cmd values:\n");
37	printf("   sign                 -- create signedData\n");
38	printf("   envel                -- create envelopedData\n");
39	printf("   signEnv              -- create nested EnvelopedData(signedData(data))\n");
40	printf("   certs                -- create certs-only CMS msg\n");
41	printf("   parse                -- parse a CMS message file\n");
42	printf("Input/output options:\n");
43	printf("   -i infile\n");
44	printf("   -o outfile\n");
45	printf("   -D detachedContent   -- detached content (parse only)\n");
46	printf("   -d detached          -- infile contains detached content (sign only)\n");
47	printf("   -f certFileBase      -- dump all certs to certFileBase\n");
48	printf("Signer and recipient options:\n");
49	printf("   -k keychain          -- Keychain to search for certs\n");
50	printf("   -p                   -- Use identity picker\n");
51	printf("   -r recipient         -- add recipient (via email address) of enveloped data\n");
52	printf("   -R recipCertFile     -- add recipient (via cert from file) of enveloped data\n");
53	printf("   -S signerEmail       -- add signer email address\n");
54	printf("   -C cert              -- add (general) signedData cert\n");
55	printf("Misc. options:\n");
56	printf("   -e eContentType      -- a(uthData)|r(keyData)\n");
57	printf("   -m                   -- multi updates; default is one-shot\n");
58	printf("   -1 (one)             -- custom encoder/decoder\n");
59	printf("   -2                   -- fetch SecCmsMessageRef\n");
60	printf("   -c                   -- parse signer certs\n");
61	printf("   -a [ceEt]            -- Signed Attributes: c=SmimeCaps,\n");
62	printf("                           e=EncrPrefs, E=MSEncrPrefs, t=signingTime\n");
63	printf("   -A anchorFile        -- Verify certs using specified anchor cert\n");
64	printf("   -M                   -- Do SecTrustEvaluate manually\n");
65	printf("   -t certChainMode     -- none|signer|chain|chainWithRoot; default is chain\n");
66	printf("   -l                   -- loop & pause for malloc debug\n");
67	printf("   -q                   -- quiet\n");
68	printf("   -Z                   -- silent, no output at all except for errors\n");
69	printf("Verification options:\n");
70	printf("   -v sign|encr|signEnv -- verify message is signed/encrypted/both\n");
71	printf("   -s numSigners        -- verify msg has specified number of signers\n");
72	printf("   -E eContentType      -- verify a(authData)|r(keyData)|d(data)\n");
73	printf("   -N numCerts          -- verify number of certs\n");
74	exit(1);
75}
76
77/* high level op */
78typedef enum {
79	CTO_Sign,
80	CTO_Envelop,
81	CTO_SignEnvelop,
82	CTO_CertsOnly,
83	CTO_Parse
84} CT_Op;
85
86/* to verify */
87typedef enum {
88	CTV_None,
89	CTV_Sign,
90	CTV_Envelop,
91	CTV_SignEnvelop
92} CT_Vfy;
93
94/* additional OIDS to specify as eContentType */
95#define OID_PKINIT	0x2B, 6, 1, 5, 2, 3
96#define OID_PKINIT_LEN	6
97
98static const uint8	OID_PKINIT_AUTH_DATA[]	    = {OID_PKINIT, 1};
99static const uint8	OID_PKINIT_DH_KEY_DATA[]    = {OID_PKINIT, 2};
100static const uint8	OID_PKINIT_RKEY_DATA[]	    = {OID_PKINIT, 3};
101static const uint8	OID_PKINIT_KP_CLIENTAUTH[]  = {OID_PKINIT, 3};
102static const uint8	OID_PKINIT_KPKDC[]			= {OID_PKINIT, 5};
103
104static const CSSM_OID	CSSMOID_PKINIT_AUTH_DATA =
105	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA};
106static const CSSM_OID	CSSMOID_PKINIT_DH_KEY_DATA =
107	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA};
108static const CSSM_OID	CSSMOID_PKINIT_RKEY_DATA =
109	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA};
110static const CSSM_OID	CSSMOID_PKINIT_KP_CLIENTAUTH =
111	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH};
112static const CSSM_OID	CSSMOID_PKINIT_KPKDC =
113	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC};
114
115/*
116 * Find a cert in specified keychain or keychain list matching specified
117 * email address. We happen to know that the email address is stored with the
118 * kSecAlias attribute.
119 */
120static OSStatus findCert(
121	const char *emailAddress,
122	CFTypeRef kcArArray,			// kc, array, or even NULL
123	SecCertificateRef *cert)
124{
125	OSStatus					ortn;
126	SecKeychainSearchRef		srch;
127	SecKeychainAttributeList	attrList;
128	SecKeychainAttribute		attr;
129
130	attr.tag = kSecAlias;
131	attr.length = strlen(emailAddress);
132	attr.data = (void *)emailAddress;
133	attrList.count = 1;
134	attrList.attr = &attr;
135
136	ortn = SecKeychainSearchCreateFromAttributes(kcArArray,
137		kSecCertificateItemClass,
138		&attrList,
139		&srch);
140	if(ortn) {
141		cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
142		return ortn;
143	}
144
145	ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert);
146	if(ortn) {
147		printf("***No certs found matching recipient %s. Aborting.\n",
148			emailAddress);
149		return ortn;
150	}
151	CFRelease(srch);
152	return noErr;
153}
154
155/* create a SecCertificateRef from a file */
156static SecCertificateRef readCertFile(
157	const char *fileName)
158{
159	unsigned char *certData = NULL;
160	unsigned certDataLen;
161	SecCertificateRef rtnCert = NULL;
162
163	if(readFile(fileName, &certData, &certDataLen)) {
164		printf("***Error reading %s. Aborting.\n", fileName);
165		return NULL;
166	}
167	CSSM_DATA cssmCert = {certDataLen, (uint8 *)certData};
168	OSStatus ortn = SecCertificateCreateFromData(&cssmCert,
169		CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
170		&rtnCert);
171	if(ortn) {
172		cssmPerror("SecCertificateCreateFromData", ortn);
173		printf("***Error creating cert fromn %s. Aborting.\n", fileName);
174	}
175	free(certData);
176	return rtnCert;
177}
178
179static int dumpCertFiles(
180	CFArrayRef allCerts,
181	const char *fileBase,
182	bool quiet)
183{
184	char fileName[200];
185
186	if(allCerts == NULL) {
187		printf("...no certs to write.\n");
188		return 0;
189	}
190	CFIndex numCerts = CFArrayGetCount(allCerts);
191	if(numCerts == 0) {
192		printf("...no certs to write.\n");
193		return 0;
194	}
195	for(CFIndex dex=0; dex<numCerts; dex++) {
196		SecCertificateRef secCert =
197			(SecCertificateRef)CFArrayGetValueAtIndex(allCerts, dex);
198		CSSM_DATA certData;
199		OSStatus ortn;
200
201		ortn = SecCertificateGetData(secCert, &certData);
202		if(ortn) {
203			cssmPerror("SecCertificateGetData", ortn);
204			return -1;
205		}
206		sprintf(fileName, "%s_%u.cer", fileBase, (unsigned)dex);
207		if(writeFile(fileName, certData.Data, certData.Length)) {
208			printf("***Error writing cert data to %s. Aborting.\n", fileName);
209			return 1;
210		}
211		else if(!quiet) {
212			printf("...wrote %u bytes to %s.\n",
213				(unsigned)certData.Length, fileName);
214		}
215	}
216	return 0;
217}
218
219/*
220 * Do a random number of random-sized updates on a CMSEncoder.
221 */
222static OSStatus updateEncoder(
223	CMSEncoderRef cmsEncoder,
224	const unsigned char *inData,
225	unsigned inDataLen)
226{
227	unsigned toMove = inDataLen;
228	unsigned thisMove;
229
230	while(toMove != 0) {
231		thisMove = genRand(1, toMove);
232		OSStatus ortn = CMSEncoderUpdateContent(cmsEncoder, inData, thisMove);
233		if(ortn) {
234			cssmPerror("CMSEncoderUpdateContent", ortn);
235			return ortn;
236		}
237		toMove -= thisMove;
238		inData += thisMove;
239	}
240	return noErr;
241}
242
243/*
244 * Do a random number of random-sized updates on a CMSDecoder.
245 */
246static OSStatus updateDecoder(
247	CMSDecoderRef cmsDecoder,
248	const unsigned char *inData,
249	unsigned inDataLen)
250{
251	unsigned toMove = inDataLen;
252	unsigned thisMove;
253
254	while(toMove != 0) {
255		thisMove = genRand(1, toMove);
256		OSStatus ortn = CMSDecoderUpdateMessage(cmsDecoder, inData, thisMove);
257		if(ortn) {
258			cssmPerror("CMSDecoderUpdateMessage", ortn);
259			return ortn;
260		}
261		toMove -= thisMove;
262		inData += thisMove;
263	}
264	return noErr;
265}
266
267#define TRUST_STRING_MAX	128
268
269static OSStatus evalSecTrust(
270	SecTrustRef			secTrust,
271	CFMutableArrayRef	anchorArray,		// optional
272	char				*trustStr,			// caller-mallocd, TRUST_STRING_MAX chars
273	bool				quiet)
274{
275	OSStatus ortn;
276	SecTrustResultType			secTrustResult;
277
278	if(anchorArray) {
279		ortn  = SecTrustSetAnchorCertificates(secTrust, anchorArray);
280		if(ortn) {
281			/* should never happen */
282			cssmPerror("SecTrustSetAnchorCertificates", ortn);
283			return ortn;
284		}
285	}
286	ortn = SecTrustEvaluate(secTrust, &secTrustResult);
287	if(ortn) {
288		/* should never happen */
289		cssmPerror("SecTrustEvaluate", ortn);
290		return ortn;
291	}
292	switch(secTrustResult) {
293		case kSecTrustResultUnspecified:
294			/* cert chain valid, no special UserTrust assignments */
295		case kSecTrustResultProceed:
296			/* cert chain valid AND user explicitly trusts this */
297			sprintf(trustStr, "Successful\n");
298			return noErr;
299		case kSecTrustResultDeny:
300		case kSecTrustResultConfirm:
301			/*
302			 * Cert chain may well have verified OK, but user has flagged
303			 * one of these certs as untrustable.
304			 */
305			sprintf(trustStr, "Not trusted per user-specified Trust level\n");
306			/* bogus return code I know */
307			return errSecInvalidTrustSetting;
308		default:
309		{
310			/* get low-level TP error */
311			OSStatus tpStatus;
312			ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus);
313			if(ortn) {
314				cssmPerror("SecTrustGetCssmResultCode", ortn);
315				return ortn;
316			}
317			switch(tpStatus) {
318				case CSSMERR_TP_INVALID_ANCHOR_CERT:
319					sprintf(trustStr, "Untrusted root\n");
320					break;
321				case CSSMERR_TP_NOT_TRUSTED:
322					/* no root, not even in implicit SSL roots */
323					sprintf(trustStr, "No root cert found\n");
324					break;
325				case CSSMERR_TP_CERT_EXPIRED:
326					sprintf(trustStr, "Expired cert\n");
327					break;
328				case CSSMERR_TP_CERT_NOT_VALID_YET:
329					sprintf(trustStr, "Cert not valid yet\n");
330					break;
331				default:
332					sprintf(trustStr, "Other cert failure (%s)",
333						cssmErrToStr(tpStatus));
334					break;
335			}
336			return tpStatus;
337		}
338	} 	/* SecTrustEvaluate error */
339	/* NOT REACHED */
340}
341
342static OSStatus doParse(
343	const unsigned char *data,
344	unsigned			dataLen,
345	const unsigned char *detachedContent,
346	unsigned			detachedContentLen,
347	bool				multiUpdate,
348	CT_Vfy				vfyOp,
349	const CSSM_OID		*eContentVfy,	// optional to verify
350	int					numSignersVfy,	// optional (>=0) to verify
351	int					numCertsVfy,	// optionjal (>= 0) to verify
352	bool				parseSignerCert,
353	const char			*certFileBase,	// optionally write certs here
354	bool				customDecoder,	// invoke CMSDecoderSetDecoder()
355	bool				manTrustEval,	// evaluate SecTrust ourself
356	CFMutableArrayRef	anchorArray,	// optional, and only for manTrustEval
357	bool				quiet,
358	CFDataRef			*outData)		// RETURNED
359{
360	if((data == NULL) || (dataLen == 0)) {
361		fprintf(stderr, "***Parse requires input file. Aborting.\n");
362		return paramErr;
363	}
364
365	CMSDecoderRef cmsDecoder;
366	size_t numSigners;
367	Boolean isEncrypted;
368	CFArrayRef allCerts = NULL;
369	unsigned signerDex;
370	SecPolicyRef policy = NULL;
371	SecPolicySearchRef policySearch = NULL;
372	CFIndex numCerts = 0;
373	int addDetachedAfterDecode = 0;
374	SecArenaPoolRef arena = NULL;
375
376	/*
377	 * Four different return codes:
378	 * -- ortn used for function returns; if nonzero, bail immediately and goto errOut
379	 * -- ourRtn is manually set per the output of CMSDecoderCopySignerStatus and
380	 *    evalSecTrust
381	 * -- trustRtn is the output of manual SecTrustEvaluate (via evalSecTrust())
382	 * -- vfyErr indicates mismatch in caller-specified error params
383	 *
384	 * All four have to be zero fgor us to return zero.
385	 */
386	OSStatus ourRtn = noErr;
387	OSStatus ortn = noErr;
388	OSStatus trustRtn = noErr;
389	int vfyErr = 0;
390
391	ortn = CMSDecoderCreate(&cmsDecoder);
392	if(ortn) {
393		cssmPerror("CMSDecoderCreate", ortn);
394		return ortn;
395	}
396
397	/* subsequent errors to errOut: */
398
399	if(detachedContent != NULL) {
400		/*
401		 * We can add detached content either before or after the
402		 * update/finalize; to test and verify, flip a coin to decide
403		 * when to do it.
404		 */
405		addDetachedAfterDecode = genRand(0, 1);
406		if(!addDetachedAfterDecode) {
407			CFDataRef cfDetach = CFDataCreate(NULL, detachedContent, detachedContentLen);
408			ortn = CMSDecoderSetDetachedContent(cmsDecoder, cfDetach);
409			CFRelease(cfDetach);
410			if(ortn) {
411				cssmPerror("CMSDecoderSetDetachedContent", ortn);
412				goto errOut;
413			}
414		}
415	}
416
417	if(customDecoder) {
418		/* Create a decoder; we don't have to free it, but we do have to free the
419		 * arena pool */
420		SecCmsDecoderRef coder = NULL;
421
422		ortn = SecArenaPoolCreate(1024, &arena);
423		if(ortn) {
424			cssmPerror("SecArenaPoolCreate", ortn);
425			goto errOut;
426		}
427		ortn = SecCmsDecoderCreate(arena,
428			NULL, NULL, NULL, NULL, NULL, NULL, &coder);
429		if(ortn) {
430			cssmPerror("SecCmsDecoderCreate", ortn);
431			goto errOut;
432		}
433		ortn = CMSDecoderSetDecoder(cmsDecoder, coder);
434		if(ortn) {
435			cssmPerror("CMSDecoderSetDecoder", ortn);
436			goto errOut;
437		}
438		else if(!quiet) {
439			printf("...set up custom SecCmsDecoderRef\n");
440		}
441	}
442	if(multiUpdate) {
443		ortn = updateDecoder(cmsDecoder, data, dataLen);
444		if(ortn) {
445			goto errOut;
446		}
447	}
448	else {
449		ortn = CMSDecoderUpdateMessage(cmsDecoder, data, dataLen);
450		if(ortn) {
451			cssmPerror("CMSDecoderUpdateMessage", ortn);
452			goto errOut;
453		}
454	}
455	ortn = CMSDecoderFinalizeMessage(cmsDecoder);
456	if(ortn) {
457		cssmPerror("CMSDecoderFinalizeMessage", ortn);
458		goto errOut;
459	}
460	if(addDetachedAfterDecode) {
461		CFDataRef cfDetach = CFDataCreate(NULL, detachedContent, detachedContentLen);
462		ortn = CMSDecoderSetDetachedContent(cmsDecoder, cfDetach);
463		CFRelease(cfDetach);
464		if(ortn) {
465			cssmPerror("CMSDecoderSetDetachedContent", ortn);
466			goto errOut;
467		}
468	}
469	ortn = CMSDecoderGetNumSigners(cmsDecoder, &numSigners);
470	if(ortn) {
471		cssmPerror("CMSDecoderGetNumSigners", ortn);
472		goto errOut;
473	}
474	ortn = CMSDecoderIsContentEncrypted(cmsDecoder, &isEncrypted);
475	if(ortn) {
476		cssmPerror("CMSDecoderIsContentEncrypted", ortn);
477		goto errOut;
478	}
479	ortn = CMSDecoderCopyAllCerts(cmsDecoder, &allCerts);
480	if(ortn) {
481		cssmPerror("CMSDecoderCopyAllCerts", ortn);
482		goto errOut;
483	}
484	if(allCerts) {
485		numCerts = CFArrayGetCount(allCerts);
486	}
487
488	/* optional verify of expected message type */
489	switch(vfyOp) {
490		case CTV_None:
491			break;
492		case CTV_Sign:
493			if((numSigners == 0) && (allCerts == NULL)) {
494				fprintf(stderr, "***Expected SignedData, but no signersFound\n");
495				vfyErr = 1;
496				/* but keep going */
497			}
498			if(isEncrypted) {
499				fprintf(stderr, "***Expected SignedData, but msg IS encrypted\n");
500				vfyErr = 1;
501			}
502			break;
503		case CTV_SignEnvelop:
504			if(numSigners == 0) {
505				fprintf(stderr, "***Expected Signed&Enveloped, but no signersFound\n");
506				vfyErr = 1;
507			}
508			if(!isEncrypted) {
509				fprintf(stderr, "***Expected Signed&Enveloped, but msg not encrypted\n");
510				vfyErr = 1;
511			}
512			break;
513		case CTV_Envelop:
514			if(numSigners != 0) {
515				fprintf(stderr, "***Expected EnvelopedData, but signers found\n");
516				vfyErr = 1;
517			}
518			if(!isEncrypted) {
519				fprintf(stderr, "***Expected EnvelopedData, but msg not encrypted\n");
520				vfyErr = 1;
521			}
522			break;
523	}
524
525	if(numSignersVfy >= 0) {
526		if((unsigned)numSignersVfy != numSigners) {
527			fprintf(stderr, "***Expected %d signers; found %lu\n",
528				numSignersVfy, numSigners);
529			vfyErr = 1;
530		}
531	}
532	if(numCertsVfy >= 0) {
533		if((int)numCerts != numCertsVfy) {
534			fprintf(stderr, "***Expected %d certs; found %d\n",
535				numCertsVfy, (int)numCerts);
536			vfyErr = 1;
537		}
538	}
539	if(!quiet) {
540		fprintf(stderr, "=== CMS message info ===\n");
541		fprintf(stderr, "   Num Signers      : %lu\n", (unsigned long)numSigners);
542		fprintf(stderr, "   Encrypted        : %s\n", isEncrypted ? "true" : "false");
543		fprintf(stderr, "   Num Certs        : %lu\n",
544			allCerts ? (unsigned long)CFArrayGetCount(allCerts) : 0);
545	}
546
547	if((certFileBase != NULL) & (allCerts != NULL)) {
548		dumpCertFiles(allCerts, certFileBase, quiet);
549	}
550
551
552	if(numSigners) {
553		CSSM_OID eContentType = {0, NULL};
554		CFDataRef eContentData = NULL;
555		OidParser oidParser;
556		char str[OID_PARSER_STRING_SIZE];
557
558		ortn = CMSDecoderCopyEncapsulatedContentType(cmsDecoder, &eContentData);
559		if(ortn) {
560			cssmPerror("CMSDecoderCopyEncapsulatedContentType", ortn);
561			goto errOut;
562		}
563		if(eContentData != NULL) {
564			eContentType.Data = (uint8 *)CFDataGetBytePtr(eContentData);
565			eContentType.Length = CFDataGetLength(eContentData);
566		}
567		if(!quiet) {
568			/* can't use stderr - oidparser is fixed w/stdout */
569			printf("   eContentType     : ");
570			if(eContentType.Data == NULL) {
571				printf("***NONE FOUND***\n");
572			}
573			else if(eContentType.Length == 0) {
574				printf("***EMPTY***\n");
575			}
576			else {
577				oidParser.oidParse(eContentType.Data, eContentType.Length, str);
578				printf("%s\n", str);
579			}
580		}
581
582		if(eContentVfy != NULL) {
583			if(eContentType.Data == NULL) {
584				fprintf(stderr, "***Tried to verify eContentType, but none found\n");
585				vfyErr = 1;
586			}
587			else if(!appCompareCssmData(eContentVfy, &eContentType)) {
588				fprintf(stderr, "***eContentType verify error\n");
589				fprintf(stderr, "   Expected: ");
590				oidParser.oidParse(eContentVfy->Data, eContentVfy->Length, str);
591				printf("%s\n", str);
592				fprintf(stderr, "   Found   : ");
593				oidParser.oidParse(eContentType.Data, eContentType.Length, str);
594				printf("%s\n", str);
595				vfyErr = 1;
596			}
597		}
598		CFRELEASE(eContentData);
599
600		/* get a policy for cert evaluation */
601		ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
602			&CSSMOID_APPLE_X509_BASIC,
603			NULL,
604			&policySearch);
605		if(ortn) {
606			cssmPerror("SecPolicySearchCreate", ortn);
607			goto errOut;
608		}
609		ortn = SecPolicySearchCopyNext(policySearch, &policy);
610		if(ortn) {
611			cssmPerror("SecPolicySearchCopyNext", ortn);
612			goto errOut;
613		}
614	}
615	for(signerDex=0; signerDex<numSigners; signerDex++) {
616		CMSSignerStatus		signerStatus = kCMSSignerInvalidIndex;
617		SecCertificateRef	signerCert;
618		CFStringRef			signerEmailAddress;
619		SecTrustRef			secTrust;
620		OSStatus			certVerifyResultCode;
621		char				trustStr[TRUST_STRING_MAX];
622
623		ortn = CMSDecoderCopySignerStatus(cmsDecoder, signerDex,
624			policy,
625			manTrustEval ? FALSE : TRUE,	/* evaluateSecTrust */
626			&signerStatus,
627			&secTrust,
628			&certVerifyResultCode);
629		if(ortn) {
630			cssmPerror("CMSDecoderCopySignerStatus", ortn);
631			goto errOut;
632		}
633		if(ourRtn == noErr) {
634			if((signerStatus != kCMSSignerValid) ||
635			   (certVerifyResultCode != CSSM_OK)) {
636				ourRtn = -1;
637			}
638		}
639		ortn = CMSDecoderCopySignerEmailAddress(cmsDecoder, signerDex,
640			&signerEmailAddress);
641		if(ortn) {
642			cssmPerror("CMSDecoderCopySignerEmailAddress", ortn);
643			goto errOut;
644		}
645		ortn = CMSDecoderCopySignerCert(cmsDecoder, signerDex,
646			&signerCert);
647		if(ortn) {
648			cssmPerror("CMSDecoderCopySignerCertificate", ortn);
649			goto errOut;
650		}
651		if(manTrustEval) {
652			trustRtn = evalSecTrust(secTrust, anchorArray, trustStr, quiet);
653		}
654
655		/* display nothing here if quiet true and status is copacetic */
656		if(!quiet || (signerStatus != kCMSSignerValid) || (trustRtn != noErr)) {
657			fprintf(stderr, "   Signer %u:\n", signerDex);
658			fprintf(stderr, "      signerStatus  : ");
659			switch(signerStatus) {
660				case kCMSSignerUnsigned:
661					fprintf(stderr, "kCMSSignerUnsigned\n"); break;
662				case kCMSSignerValid:
663					fprintf(stderr, "kCMSSignerValid\n"); break;
664				case kCMSSignerNeedsDetachedContent:
665					fprintf(stderr, "kCMSSignerNeedsDetachedContent\n"); break;
666				case kCMSSignerInvalidSignature:
667					fprintf(stderr, "kCMSSignerInvalidSignature\n"); break;
668				case kCMSSignerInvalidCert:
669					fprintf(stderr, "kCMSSignerInvalidCert\n"); break;
670				case kCMSSignerInvalidIndex:
671					fprintf(stderr, "kCMSSignerInvalidIndex\n"); break;
672			}
673			if(manTrustEval) {
674				fprintf(stderr, "      Trust Eval    : %s\n", trustStr);
675			}
676			fprintf(stderr, "      emailAddrs    : ");
677			if(signerEmailAddress == NULL) {
678				fprintf(stderr, "<<none found>>\n");
679			}
680			else {
681				char emailStr[1000];
682				if(!CFStringGetCString(signerEmailAddress,
683						emailStr, 1000, kCFStringEncodingASCII)) {
684					fprintf(stderr, "<<<Error converting email address to C string>>>\n");
685				}
686				else {
687					fprintf(stderr, "%s\n", emailStr);
688				}
689			}
690
691			fprintf(stderr, "      vfyResult     : %s\n",
692				certVerifyResultCode ?
693					cssmErrToStr(certVerifyResultCode) : "Success");
694
695			/* TBD: optionally manually verify the SecTrust object */
696
697			if(parseSignerCert) {
698
699				if(signerCert == NULL) {
700					fprintf(stderr, "      <<<Unable to obtain signer cert>>>\n");
701				}
702				else {
703					CSSM_DATA certData;
704					ortn = SecCertificateGetData(signerCert, &certData);
705					if(ortn) {
706						fprintf(stderr, "      <<<Unable to obtain signer cert>>>\n");
707						cssmPerror("SecCertificateGetData", ortn);
708					}
709					else {
710						printf("========== Signer Cert==========\n\n");
711						printCert(certData.Data, certData.Length, CSSM_FALSE);
712						printf("========== End Signer Cert==========\n\n");
713					}
714				}
715			}	/* parseSignerCert */
716		}	/* displaying per-signer info */
717
718		CFRELEASE(signerCert);
719		signerCert = NULL;
720		CFRELEASE(signerEmailAddress);
721		signerEmailAddress = NULL;
722		CFRELEASE(secTrust);
723		secTrust = NULL;
724	}	/* for signerDex */
725
726	if(ortn == noErr) {
727		ortn = CMSDecoderCopyContent(cmsDecoder, outData);
728		if(ortn) {
729			cssmPerror("CMSDecoderCopyContent", ortn);
730		}
731	}
732
733errOut:
734	CFRelease(cmsDecoder);
735	if(arena != NULL) {
736		SecArenaPoolFree(arena, false);
737	}
738
739	CFRELEASE(allCerts);
740	CFRELEASE(policySearch);
741	CFRELEASE(policy);
742	if(ourRtn) {
743		return ourRtn;
744	}
745	else if(trustRtn) {
746		return trustRtn;
747	}
748	else if(vfyErr) {
749		return vfyErr;
750	}
751	else {
752		return ortn;
753	}
754}
755
756static OSStatus doSign(
757	CFTypeRef			signerOrArray,
758	const unsigned char *inData,
759	unsigned			inDataLen,
760	bool				multiUpdate,
761	bool				detachedContent,
762	const CSSM_OID		*eContentType,	// OPTIONAL
763	CMSSignedAttributes	attrs,
764	CFTypeRef			otherCerts,		// OPTIONAL
765	bool				customCoder,
766	bool				getCmsMsg,
767	CMSCertificateChainMode chainMode,
768	bool				quiet,
769	CFDataRef			*outData)		// RETURNED
770{
771	if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
772		fprintf(stderr, "***Sign requires input file. Aborting.\n");
773		return paramErr;
774	}
775	if(signerOrArray == NULL) {
776		fprintf(stderr, "***Sign requires a signing identity. Aborting.\n");
777		return paramErr;
778	}
779
780	OSStatus ortn;
781	CMSEncoderRef cmsEncoder = NULL;
782	SecCmsMessageRef msg = NULL;		/* for optional CMSEncoderGetCmsMessage */
783	SecArenaPoolRef arena = NULL;
784	CSSM_DATA encoderOut = {0, NULL};
785
786	if(multiUpdate || otherCerts || getCmsMsg || customCoder ||
787			(chainMode != kCMSCertificateChain)) {
788		/* one-shot encode doesn't support otherCerts or chainOptions*/
789
790		ortn = CMSEncoderCreate(&cmsEncoder);
791		if(ortn) {
792			cssmPerror("CMSEncoderCreate", ortn);
793			return ortn;
794		}
795		/* subsequent errors to errOut: */
796		if(signerOrArray != NULL) {
797			ortn = CMSEncoderAddSigners(cmsEncoder, signerOrArray);
798			if(ortn) {
799				goto errOut;
800			}
801		}
802		if(eContentType) {
803			ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
804			if(ortn) {
805				goto errOut;
806			}
807		}
808		if(detachedContent) {
809			ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent);
810			if(ortn) {
811				goto errOut;
812			}
813		}
814		if(otherCerts) {
815			ortn = CMSEncoderAddSupportingCerts(cmsEncoder, otherCerts);
816			if(ortn) {
817				goto errOut;
818			}
819		}
820		if(attrs) {
821			ortn = CMSEncoderAddSignedAttributes(cmsEncoder, attrs);
822			if(ortn) {
823				goto errOut;
824			}
825		}
826		if(chainMode != kCMSCertificateChain) {
827			ortn = CMSEncoderSetCertificateChainMode(cmsEncoder, chainMode);
828			if(ortn) {
829				goto errOut;
830			}
831		}
832		if(getCmsMsg || customCoder) {
833			/*
834			 * We just want to trigger the state transition
835			 * that we know should happen. We also might need
836			 * the msg to create a custom coder.
837			 */
838			ortn = CMSEncoderGetCmsMessage(cmsEncoder, &msg);
839			if(ortn) {
840				cssmPerror("CMSEncoderGetCmsMessage", ortn);
841				goto errOut;
842			}
843		}
844
845		if(customCoder) {
846			SecCmsEncoderRef coder = NULL;
847			ortn = SecArenaPoolCreate(1024, &arena);
848			if(ortn) {
849				cssmPerror("SecArenaPoolCreate", ortn);
850				goto errOut;
851			}
852			ortn = SecCmsEncoderCreate(msg,
853				NULL, NULL,		// no callback
854				&encoderOut,	// data goes here
855				arena,
856				NULL, NULL,		// no password callback (right?)
857				NULL, NULL,		// decrypt key callback
858				NULL, NULL,		// detached digests
859				&coder);
860			if(ortn) {
861				cssmPerror("SecCmsEncoderCreate", ortn);
862				goto errOut;
863			}
864			ortn = CMSEncoderSetEncoder(cmsEncoder, coder);
865			if(ortn) {
866				cssmPerror("CMSEncoderSetEncoder", ortn);
867				goto errOut;
868			}
869			else if(!quiet) {
870				printf("...set up custom SecCmsEncoderRef\n");
871			}
872		}
873		/* random number of random-sized updates */
874		ortn = updateEncoder(cmsEncoder, inData, inDataLen);
875		if(ortn) {
876			goto errOut;
877		}
878
879		ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
880		if(ortn) {
881			cssmPerror("CMSEncoderCopyEncodedContent", ortn);
882		}
883		if(customCoder) {
884			/* we have the data right here */
885			*outData = 	CFDataCreate(NULL,
886				(const UInt8 *)encoderOut.Data,	encoderOut.Length);
887		}
888	}
889	else {
890		ortn = CMSEncode(signerOrArray,
891			NULL,			/* recipients */
892			eContentType,
893			detachedContent,
894			attrs,
895			inData, inDataLen,
896			outData);
897		if(ortn) {
898			printf("***CMSEncode returned %ld\n", (long)ortn);
899			cssmPerror("CMSEncode", ortn);
900		}
901	}
902errOut:
903	if(cmsEncoder) {
904		CFRelease(cmsEncoder);
905	}
906	if(arena) {
907		SecArenaPoolFree(arena, false);
908	}
909	return ortn;
910}
911
912static OSStatus doEncrypt(
913	CFTypeRef			recipOrArray,
914	const unsigned char *inData,
915	unsigned			inDataLen,
916	bool				multiUpdate,
917	CFDataRef			*outData)		// RETURNED
918{
919	if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
920		fprintf(stderr, "***Encrypt requires input file. Aborting.\n");
921		return paramErr;
922	}
923	if(recipOrArray == NULL) {
924		fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n");
925		return paramErr;
926	}
927
928	OSStatus ortn;
929	CMSEncoderRef cmsEncoder = NULL;
930
931	if(multiUpdate) {
932		ortn = CMSEncoderCreate(&cmsEncoder);
933		if(ortn) {
934			cssmPerror("CMSEncoderCreate", ortn);
935			return ortn;
936		}
937		/* subsequent errors to errOut: */
938		ortn = CMSEncoderAddRecipients(cmsEncoder, recipOrArray);
939		if(ortn) {
940			goto errOut;
941		}
942
943		/* random number of random-sized updates */
944		ortn = updateEncoder(cmsEncoder, inData, inDataLen);
945		if(ortn) {
946			goto errOut;
947		}
948		ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
949		if(ortn) {
950			cssmPerror("CMSEncoderCopyEncodedContent", ortn);
951		}
952	}
953	else {
954		/* one-shot */
955		ortn = CMSEncode(NULL,	/* signers */
956			recipOrArray,
957			NULL,				/* eContentType */
958			FALSE,				/* detachedContent */
959			kCMSAttrNone,
960			inData, inDataLen,
961			outData);
962		if(ortn) {
963			printf("***CMSEncode returned %ld\n", (long)ortn);
964			cssmPerror("CMSEncode", ortn);
965		}
966	}
967errOut:
968	if(cmsEncoder) {
969		CFRelease(cmsEncoder);
970	}
971	return ortn;
972}
973
974/* create nested message: msg = EnvelopedData(SignedData(inData)) */
975static OSStatus doSignEncrypt(
976	CFTypeRef			recipOrArray,	// encryption recipients
977	CFTypeRef			signerOrArray,	// signers
978	const CSSM_OID		*eContentType,	// OPTIONAL - for signedData
979	CMSSignedAttributes	attrs,
980	const unsigned char *inData,
981	unsigned			inDataLen,
982	bool				multiUpdate,
983	CFTypeRef			otherCerts,		// OPTIONAL
984	CFDataRef			*outData)		// RETURNED
985{
986	if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
987		fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n");
988		return paramErr;
989	}
990	if(recipOrArray == NULL) {
991		fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n");
992		return paramErr;
993	}
994	if(signerOrArray == NULL) {
995		fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n");
996		return paramErr;
997	}
998
999	OSStatus ortn;
1000	CMSEncoderRef cmsEncoder = NULL;
1001
1002	if(multiUpdate || otherCerts) {
1003		ortn = CMSEncoderCreate(&cmsEncoder);
1004		if(ortn) {
1005			cssmPerror("CMSEncoderCreate", ortn);
1006			return ortn;
1007		}
1008		/* subsequent errors to errOut: */
1009		ortn = CMSEncoderAddRecipients(cmsEncoder, recipOrArray);
1010		if(ortn) {
1011			goto errOut;
1012		}
1013		ortn = CMSEncoderAddSigners(cmsEncoder, signerOrArray);
1014		if(ortn) {
1015			goto errOut;
1016		}
1017		if(eContentType) {
1018			ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
1019			if(ortn) {
1020				goto errOut;
1021			}
1022		}
1023		if(otherCerts) {
1024			ortn = CMSEncoderAddSupportingCerts(cmsEncoder, otherCerts);
1025			if(ortn) {
1026				goto errOut;
1027			}
1028		}
1029		if(attrs) {
1030			ortn = CMSEncoderAddSignedAttributes(cmsEncoder, attrs);
1031			if(ortn) {
1032				goto errOut;
1033			}
1034		}
1035
1036		/* random number of random-sized updates */
1037		ortn = updateEncoder(cmsEncoder, inData, inDataLen);
1038		if(ortn) {
1039			goto errOut;
1040		}
1041		ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
1042		if(ortn) {
1043			cssmPerror("CMSEncoderCopyEncodedContent", ortn);
1044		}
1045	}
1046	else {
1047		ortn = CMSEncode(signerOrArray,
1048			recipOrArray,
1049			eContentType,
1050			FALSE,				/* detachedContent */
1051			attrs,
1052			inData, inDataLen,
1053			outData);
1054		if(ortn) {
1055			printf("***CMSEncode returned %ld\n", (long)ortn);
1056			cssmPerror("CMSEncode", ortn);
1057		}
1058	}
1059
1060errOut:
1061	if(cmsEncoder) {
1062		CFRelease(cmsEncoder);
1063	}
1064	return ortn;
1065}
1066
1067/*
1068 * Create a CMS message containing only certs.
1069 */
1070static OSStatus makeCertBag(
1071	CFTypeRef	certsOrArray,
1072	CFDataRef	*outData)
1073{
1074	if(certsOrArray == NULL) {
1075		printf("***Need some certs to generate this type of message.\n");
1076		return -1;
1077	}
1078
1079	OSStatus ortn;
1080	CMSEncoderRef cmsEncoder = NULL;
1081
1082	ortn = CMSEncoderCreate(&cmsEncoder);
1083	if(ortn) {
1084		cssmPerror("CMSEncoderCreate", ortn);
1085		return ortn;
1086	}
1087	/* subsequent errors to errOut: */
1088	ortn = CMSEncoderAddSupportingCerts(cmsEncoder, certsOrArray);
1089	if(ortn) {
1090		goto errOut;
1091	}
1092	ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
1093	if(ortn) {
1094		cssmPerror("CMSEncoderCopyEncodedContent", ortn);
1095	}
1096errOut:
1097	CFRelease(cmsEncoder);
1098	return ortn;
1099}
1100
1101/*
1102 * Support maintanance of single item or array of them.
1103 *
1104 * Given new incoming 'newThing':
1105 *   if both *thingArray and *currThing are NULL
1106 *		*currThing = newThing;
1107 *		done;
1108 *   create *thingArray;
1109 *   add *currThing to *thingArray if present;
1110 *   add newThing to *thingArray;
1111 */
1112static void addThing(
1113	CFTypeRef newThing,
1114	CFTypeRef *currThing,
1115	CFMutableArrayRef *thingArray)
1116{
1117	if((*currThing == NULL) && (*thingArray == NULL)) {
1118		/* first occurrence of a thing */
1119		*currThing = newThing;
1120		return;
1121	}
1122
1123	/* at least two things - prepare array */
1124	if(*thingArray == NULL) {
1125		*thingArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1126	}
1127	if(*currThing != NULL) {
1128		/* move current thing to array */
1129		CFArrayAppendValue(*thingArray, *currThing);
1130		CFRelease(*currThing);
1131		*currThing = NULL;
1132	}
1133	CFArrayAppendValue(*thingArray, newThing);
1134	CFRelease(newThing);
1135}
1136
1137int main(int argc, char **argv)
1138{
1139	if(argc < 2) {
1140		usage(argv);
1141	}
1142
1143	CT_Op op;
1144	bool needId = false;
1145	if(!strcmp(argv[1], "sign")) {
1146		op = CTO_Sign;
1147		needId = true;
1148	}
1149	else if(!strcmp(argv[1], "envel")) {
1150		op = CTO_Envelop;
1151	}
1152	else if(!strcmp(argv[1], "signEnv")) {
1153		op = CTO_SignEnvelop;
1154		needId = true;
1155	}
1156	else if(!strcmp(argv[1], "certs")) {
1157		op = CTO_CertsOnly;
1158	}
1159	else if(!strcmp(argv[1], "parse")) {
1160		op = CTO_Parse;
1161	}
1162	else {
1163		fprintf(stderr, "***Unrecognized cmd.\n");
1164		usage(argv);
1165	}
1166
1167	extern int optind;
1168	extern char *optarg;
1169	int arg;
1170	OSStatus ortn;
1171
1172	/* optional args */
1173	char *inFileName = NULL;
1174	char *outFileName = NULL;
1175	bool detachedContent = false;
1176	char *detachedFile = NULL;
1177	bool useIdPicker = false;
1178	bool quiet = false;
1179	bool silent = false;
1180	bool parseSignerCert = false;
1181	const CSSM_OID *eContentType = NULL;
1182	bool multiUpdate = false;
1183	bool loopPause = false;
1184	char *certFileBase = NULL;
1185	CMSSignedAttributes signedAttrs = 0;
1186	char *anchorFile = NULL;
1187	bool manTrustEval = false;
1188	CMSCertificateChainMode chainMode = kCMSCertificateChain;
1189
1190	/* for verification, usually in quiet/script mode */
1191	CT_Vfy vfyOp = CTV_None;
1192	const CSSM_OID *eContentVfy = NULL;
1193	int numSignersVfy = -1;
1194	int numCertsVfy = -1;
1195
1196	/* for verifying functions in CMSPrivate.h */
1197	bool customCoder = false;
1198	bool fetchSecCmsMsg = false;
1199
1200	/*
1201	 * Signer/recipient items and arrays - use one item if possible,
1202	 * else array (to test both paths)
1203	 */
1204	SecIdentityRef signerId = NULL;
1205	CFMutableArrayRef signerArray = NULL;
1206	SecCertificateRef recipCert = NULL;
1207	CFMutableArrayRef recipArray = NULL;
1208	SecCertificateRef generalCert = NULL;
1209	CFMutableArrayRef generalCertArray = NULL;
1210	SecKeychainRef kcRef = NULL;
1211	CFMutableArrayRef anchorArray = NULL;
1212
1213	optind = 2;
1214	while ((arg = getopt(argc, argv, "i:o:k:pr:R:dD:e:mlqcv:s:E:S:C:f:N:a:A:M12t:Z")) != -1) {
1215		switch (arg) {
1216			case 'i':
1217				inFileName = optarg;
1218				break;
1219			case 'o':
1220				outFileName = optarg;
1221				break;
1222			case 'k':
1223				kcRef = keychain_open(optarg);
1224				if(!kcRef) {
1225				//	cssmPerror("SecKeychainOpen", ortn);
1226					exit(1);
1227				}
1228				break;
1229			case 'p':
1230				useIdPicker = true;
1231				break;
1232			case 'r':
1233			{
1234				SecCertificateRef newCert = NULL;
1235				char *recipient = optarg;
1236				ortn = findCert(recipient, kcRef, &newCert);
1237				if(ortn) {
1238					exit(1);
1239				}
1240				addThing(newCert, (CFTypeRef *)&recipCert, &recipArray);
1241				break;
1242			}
1243			case 'R':
1244			{
1245				SecCertificateRef certRef = readCertFile(optarg);
1246				if(certRef == NULL) {
1247					exit(1);
1248				}
1249				addThing(certRef, (CFTypeRef *)&recipCert, &recipArray);
1250				break;
1251			}
1252			case 'S':
1253			{
1254				SecIdentityRef newId = NULL;
1255				SecCertificateRef newCert = NULL;
1256				char *signerEmail = optarg;
1257
1258				/*
1259				 * first find the cert, optionally in the keychain already
1260				 * specified via -k
1261				 */
1262				ortn = findCert(signerEmail, kcRef, &newCert);
1263				if(ortn) {
1264					exit(1);
1265				}
1266
1267				/* map cert to an identity */
1268				ortn = SecIdentityCreateWithCertificate(kcRef, newCert, &newId);
1269				if(ortn) {
1270					cssmPerror("SecIdentityCreateWithCertificate", ortn);
1271					exit(1);
1272				}
1273
1274				addThing(newId, (CFTypeRef *)&signerId, &signerArray);
1275				CFRelease(newCert);
1276				break;
1277			}
1278			case 'C':
1279			{
1280				SecCertificateRef newCert = readCertFile(optarg);
1281				if(newCert == NULL) {
1282					exit(1);
1283				}
1284				addThing(newCert, (CFTypeRef *)&generalCert, &generalCertArray);
1285				break;
1286			}
1287			case 'c':
1288				parseSignerCert = true;
1289				break;
1290			case 'v':
1291				if(!strcmp(optarg, "sign")) {
1292					vfyOp = CTV_Sign;
1293				}
1294				else if(!strcmp(optarg, "encr")) {
1295					vfyOp = CTV_Envelop;
1296				}
1297				else if(!strcmp(optarg, "signEnv")) {
1298					vfyOp = CTV_SignEnvelop;
1299				}
1300				else {
1301					usage(argv);
1302				}
1303				break;
1304			case 'e':
1305				switch(optarg[0]) {
1306					case 'a':
1307						eContentType = &CSSMOID_PKINIT_AUTH_DATA;
1308						break;
1309					case 'r':
1310						eContentType = &CSSMOID_PKINIT_RKEY_DATA;
1311						break;
1312					default:
1313						usage(argv);
1314				}
1315				break;
1316			case 'E':
1317				switch(optarg[0]) {
1318					case 'a':
1319						eContentVfy = &CSSMOID_PKINIT_AUTH_DATA;
1320						break;
1321					case 'r':
1322						eContentVfy = &CSSMOID_PKINIT_RKEY_DATA;
1323						break;
1324					case 'd':
1325						eContentVfy = &CSSMOID_PKCS7_Data;
1326						break;
1327					default:
1328						usage(argv);
1329				}
1330				break;
1331			case 'd':
1332				if(op != CTO_Sign) {
1333					printf("-d only valid for op sign\n");
1334					exit(1);
1335				}
1336				detachedContent = true;
1337				break;
1338			case 'D':
1339				if(op != CTO_Parse) {
1340					printf("-D only valid for op sign\n");
1341					exit(1);
1342				}
1343				detachedFile = optarg;
1344				break;
1345			case 'l':
1346				loopPause = true;
1347				break;
1348			case 'm':
1349				multiUpdate = true;
1350				break;
1351			case 's':
1352				numSignersVfy = atoi(optarg);
1353				break;
1354			case 'f':
1355				certFileBase = optarg;
1356				break;
1357			case 'N':
1358				numCertsVfy = atoi(optarg);
1359				break;
1360			case '1':
1361				customCoder = true;
1362				break;
1363			case '2':
1364				fetchSecCmsMsg = true;
1365				break;
1366			case 'a':
1367				for(; *optarg; optarg++) {
1368					switch(*optarg) {
1369						case 'c':
1370							signedAttrs |= kCMSAttrSmimeCapabilities;
1371							break;
1372						case 'e':
1373							signedAttrs |= kCMSAttrSmimeEncryptionKeyPrefs;
1374							break;
1375						case 'E':
1376							signedAttrs |= kCMSAttrSmimeMSEncryptionKeyPrefs;
1377							break;
1378						case 't':
1379							signedAttrs |= kCMSAttrSigningTime;
1380							break;
1381						default:
1382							usage(argv);
1383					}
1384				}
1385				break;
1386			case 'A':
1387				anchorFile = optarg;
1388				break;
1389			case 'M':
1390				manTrustEval = true;
1391				break;
1392			case 't':
1393				if(!strcmp(optarg, "none")) {
1394					chainMode = kCMSCertificateNone;
1395				}
1396				else if(!strcmp(optarg, "signer")) {
1397					chainMode = kCMSCertificateSignerOnly;
1398				}
1399				else if(!strcmp(optarg, "chain")) {
1400					chainMode = kCMSCertificateChain;
1401				}
1402				else if(!strcmp(optarg, "chainWithRoot")) {
1403					chainMode = kCMSCertificateChainWithRoot;
1404				}
1405				else {
1406					printf("***Bogus cert chain spec***\n");
1407					usage(argv);
1408				}
1409				break;
1410			case 'q':
1411				quiet = true;
1412				break;
1413			case 'Z':
1414				quiet = true;
1415				silent = true;
1416				break;
1417			default:
1418			case '?':
1419				usage(argv);
1420		}
1421	}
1422	if(optind != argc) {
1423		/* getopt does not return '?' */
1424		usage(argv);
1425	}
1426
1427	unsigned char *inData = NULL;
1428	unsigned inDataLen = 0;
1429	unsigned char *detachedData = NULL;
1430	unsigned detachedDataLen = 0;
1431	CFDataRef outData = NULL;
1432	CFIndex byteCount = 0;
1433	if(!silent) {
1434		testStartBanner((char *)"newCmsTool", argc, argv);
1435	}
1436
1437	if(inFileName) {
1438		if(readFile(inFileName, &inData, &inDataLen)) {
1439			fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName);
1440			exit(1);
1441		}
1442	}
1443	if(detachedFile) {
1444		if(readFile(detachedFile, &detachedData, &detachedDataLen)) {
1445			fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile);
1446			exit(1);
1447		}
1448	}
1449
1450	/* signer IDs */
1451	if(useIdPicker) {
1452		ortn = sslSimpleIdentPicker(kcRef, &signerId);
1453		if(ortn) {
1454			fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n");
1455			exit(1);
1456		}
1457	}
1458	if(anchorFile) {
1459		SecCertificateRef secCert = readCertFile(anchorFile);
1460		if(secCert == NULL) {
1461			exit(1);
1462		}
1463		anchorArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1464		CFArrayAppendValue(anchorArray, secCert);
1465		CFRelease(secCert);
1466	}
1467
1468	/*
1469	 * In order for signed blobs to contain a full cert chain, and to find
1470	 * certs matching a specific recipient cert email address, the keychain
1471	 * containing intermediates must be in the user's keychain search list
1472	 * (or, alternatively, the intermedaite certs must be added manually).
1473	 * To alleviate the burden on test scripts, we'll ensure that an optionally
1474	 * specified keychain is in factin the search list when we sign a message
1475	 * here.
1476	 * Careful - make sure to ALWAYS restore the search list!
1477	 */
1478	CFArrayRef originalSearchList = NULL;
1479	if(kcRef != NULL) {
1480		ortn = SecKeychainCopySearchList(&originalSearchList);
1481		if(ortn) {
1482			cssmPerror("SecKeychainCopySearchList", ortn);
1483			exit(1);
1484		}
1485		CFMutableArrayRef newList = CFArrayCreateMutableCopy(
1486			NULL, 0, originalSearchList);
1487		CFArrayAppendValue(newList, kcRef);
1488		ortn = SecKeychainSetSearchList(newList);
1489		if(ortn) {
1490			cssmPerror("SecKeychainSetSearchList", ortn);
1491			exit(1);
1492		}
1493		/* DO NOT EXIT WITHOUT RESTORING TO originalSearchList */
1494	}
1495	do {
1496		switch(op) {
1497			case CTO_Sign:
1498				ortn = doSign((signerArray != NULL) ?
1499						(CFTypeRef)signerArray : (CFTypeRef)signerId,
1500					inData, inDataLen,
1501					multiUpdate, detachedContent, eContentType, signedAttrs,
1502					(generalCertArray != NULL) ?
1503						(CFTypeRef)generalCertArray : (CFTypeRef)generalCert,
1504					customCoder, fetchSecCmsMsg, chainMode, quiet,
1505					&outData);
1506				break;
1507			case CTO_Envelop:
1508				ortn = doEncrypt(recipArray ?
1509						(CFTypeRef)recipArray : (CFTypeRef)recipCert,
1510					inData, inDataLen,
1511					multiUpdate, &outData);
1512				break;
1513			case CTO_SignEnvelop:
1514				ortn = doSignEncrypt(recipArray ?
1515						(CFTypeRef)recipArray : (CFTypeRef)recipCert,
1516					signerArray ?
1517						(CFTypeRef)signerArray : (CFTypeRef)signerId,
1518					eContentType, signedAttrs,
1519					inData, inDataLen, multiUpdate,
1520					(generalCertArray != NULL) ?
1521						(CFTypeRef)generalCertArray : (CFTypeRef)generalCert,
1522					&outData);
1523				break;
1524			case CTO_CertsOnly:
1525				ortn = makeCertBag((generalCertArray != NULL) ?
1526						(CFTypeRef)generalCertArray : (CFTypeRef)generalCert,
1527						&outData);
1528				break;
1529			case CTO_Parse:
1530				ortn = doParse(inData, inDataLen,
1531					detachedData, detachedDataLen,
1532					multiUpdate,
1533					vfyOp, eContentVfy, numSignersVfy, numCertsVfy,
1534					parseSignerCert, certFileBase, customCoder,
1535					manTrustEval, anchorArray,
1536					quiet, &outData);
1537				break;
1538		}
1539
1540		if(loopPause) {
1541			if(outData) {
1542				printf("...generated %u bytes of data.\n",
1543					(unsigned)CFDataGetLength(outData));
1544			}
1545			fpurge(stdin);
1546			printf("q to quit, anything else to loop again: ");
1547			char resp = getchar();
1548			if(resp == 'q') {
1549				break;
1550			}
1551			else {
1552				CFRELEASE(outData);
1553				outData = NULL;
1554			}
1555		}
1556	} while (loopPause);
1557
1558	if(originalSearchList) {
1559		ortn = SecKeychainSetSearchList(originalSearchList);
1560		if(ortn) {
1561			cssmPerror("SecKeychainSetSearchList", ortn);
1562			/* keep going */
1563		}
1564	}
1565
1566	if(ortn) {
1567		goto errOut;
1568	}
1569
1570	byteCount = outData ? CFDataGetLength(outData) : 0;
1571	if(outData && outFileName) {
1572		if(writeFile(outFileName, CFDataGetBytePtr(outData), byteCount)) {
1573			fprintf(stderr, "***Error writing to %s.\n", outFileName);
1574			ortn = -1;
1575		}
1576		else {
1577			if(!quiet) {
1578				fprintf(stderr, "...wrote %u bytes to %s.\n",
1579					(unsigned)byteCount, outFileName);
1580			}
1581		}
1582	}
1583	else if(byteCount) {
1584		fprintf(stderr, "...generated %u bytes but no place to write it.\n",
1585			(unsigned)byteCount);
1586	}
1587	else if(outFileName) {
1588		fprintf(stderr, "...nothing to write to file %s.\n", outFileName);
1589		/* assume this is an error, caller wanted something */
1590		ortn = -1;
1591	}
1592errOut:
1593	return ortn;
1594}
1595
1596/*
1597	From SecurityTool/keychain_utilites.cpp
1598	This properly supports dynamic keychains, i.e. smartcards
1599*/
1600
1601SecKeychainRef keychain_open(const char *name)
1602{
1603	SecKeychainRef keychain = NULL;
1604	OSStatus result;
1605
1606//	check_obsolete_keychain(name);
1607	if (name && name[0] != '/')
1608	{
1609		CFArrayRef dynamic = NULL;
1610		result = SecKeychainCopyDomainSearchList(
1611												 kSecPreferencesDomainDynamic, &dynamic);
1612		if (result)
1613		{
1614			//	cssmPerror("SecKeychainOpen", ortn);
1615		//	sec_error("SecKeychainCopyDomainSearchList %s: %s",
1616		//			  name, sec_errstr(result));
1617			cssmPerror("SecKeychainCopyDomainSearchList", result);
1618			return NULL;
1619		}
1620		else
1621		{
1622			uint32_t i;
1623			uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0;
1624
1625			for (i = 0; i < count; ++i)
1626			{
1627				char pathName[PATH_MAX];
1628				UInt32 ioPathLength = sizeof(pathName);
1629				bzero(pathName, ioPathLength);
1630				keychain = (SecKeychainRef)CFArrayGetValueAtIndex(dynamic, i);
1631				result = SecKeychainGetPath(keychain, &ioPathLength, pathName);
1632				if (result)
1633				{
1634				//	sec_error("SecKeychainGetPath %s: %s",  name, sec_errstr(result));
1635					cssmPerror("SecKeychainCopyDomainSearchList", result);
1636					return NULL;
1637				}
1638				if (!strncmp(pathName, name, ioPathLength))
1639				{
1640					CFRetain(keychain);
1641					CFRelease(dynamic);
1642					return keychain;
1643				}
1644			}
1645			CFRelease(dynamic);
1646		}
1647	}
1648
1649	result = SecKeychainOpen(name, &keychain);
1650	if (result)
1651	{
1652	//	sec_error("SecKeychainOpen %s: %s", name, sec_errstr(result));
1653		cssmPerror("SecKeychainOpen", result);
1654	}
1655
1656	return keychain;
1657}
1658
1659